Introduction

Libaries

library(dplyr)       # tidy data
library(Seurat)      # scRNA-seq infrastructure
library(ggpubr)      # plot utilities
library(celldex)     # RNA-seq reference datasets
library(ggplot2)     # plots
library(SingleR)     # cell annotations
library(decoderr)    # pathway analysis through NMF
library(SCISSORS)    # recluster scRNA-seq data
library(CONICSmat)   # estimate CNVs
library(patchwork)   # arrange plots
library(jackknife)   # my package
library(paletteer)   # all the palettes
library(kableExtra)  # table styles

Data

There are several samples here, so we’ll need to create a dictionary of the samplenames and the sample types.

dir_names <- c("GSM4679532", "GSM4679533", "GSM4679534", "GSM4679535", "GSM4679536", 
               "GSM4679537", "GSM4679538", "GSM4679539", "GSM4679540", "GSM4679541", 
               "GSM4679542", "GSM4679543", "GSM4679544", "GSM4679545", "GSM4679546", "GSM4679547")
sample_names <- c("P01", "P02", "P03", "P04", "P05", "P06", "P07", "P08", "P09", "P10", 
                  "MET01", "MET02", "MET03", "MET04", "MET05", "MET06")
sample_types <- c(rep("Primary", 10), rep("Metastatic", 6))
sample_dict <- data.frame(Directory = dir_names, 
                          Sample = sample_names, 
                          Type = sample_types)
sample_dict %>% 
  kbl(booktabs = TRUE, caption = "Sample Dictionary") %>% 
  kable_minimal("hover", html_font = "Avenir", full_width = FALSE)
Sample Dictionary
Directory Sample Type
GSM4679532 P01 Primary
GSM4679533 P02 Primary
GSM4679534 P03 Primary
GSM4679535 P04 Primary
GSM4679536 P05 Primary
GSM4679537 P06 Primary
GSM4679538 P07 Primary
GSM4679539 P08 Primary
GSM4679540 P09 Primary
GSM4679541 P10 Primary
GSM4679542 MET01 Metastatic
GSM4679543 MET02 Metastatic
GSM4679544 MET03 Metastatic
GSM4679545 MET04 Metastatic
GSM4679546 MET05 Metastatic
GSM4679547 MET06 Metastatic

Dataset Integration

First, we loop across the samples, create Seurat objects, and normalize expression and identify variable genes. The first 10 samples are primary tumors, and the next 6 are metastatic.

obj_list <- list()
for (i in seq(sample_dict$Directory)) {
  file <- paste0("~/Desktop/Data/Lin et al/GSE154778_RAW/", sample_dict$Directory[i])
  counts <- Read10X(file)
  seurat_obj <- CreateSeuratObject(counts, 
                                   project = sample_dict$Sample[i], 
                                   min.cells = 3, 
                                   min.features = 1000)
  seurat_obj@meta.data$sample <- sample_dict$Sample[i]
  seurat_obj[["percent_MT"]] <- PercentageFeatureSet(seurat_obj, pattern = "^MT-")
  seurat_obj <- SCTransform(seurat_obj, 
                            variable.features.n = 4000, 
                            vars.to.regress = "percent_MT", 
                            seed.use = 629, 
                            verbose = FALSE)
  obj_list[[i]] <- seurat_obj
}
primary_obj <- obj_list[1:10]
meta_obj <- obj_list[11:16]

Next we integrate all the samples into one Seurat object. We’ll start with the primary samples.

integration_genes <- SelectIntegrationFeatures(object.list = primary_obj, 
                                               nfeatures = 4000, 
                                               verbose = FALSE)
primary_obj <- PrepSCTIntegration(object.list = primary_obj, 
                                  anchor.features = integration_genes, 
                                  verbose = FALSE)
integration_anchors <- FindIntegrationAnchors(object.list = primary_obj, 
                                              normalization.method = "SCT", 
                                              anchor.features = integration_genes, 
                                              verbose = FALSE)
primary <- IntegrateData(anchorset = integration_anchors, 
                         normalization.method = "SCT", 
                         k.weight = 50,
                         verbose = FALSE)
primary <- CellCycleScoring(primary, 
                            s.features = cc.genes.updated.2019$s.genes, 
                            g2m.features = cc.genes.updated.2019$g2m.genes)
DefaultAssay(primary) <- "integrated"

We repeat the process for the metastatic samples.

integration_genes <- SelectIntegrationFeatures(object.list = meta_obj, 
                                               nfeatures = 4000, 
                                               verbose = FALSE)
meta_obj <- PrepSCTIntegration(object.list = meta_obj, 
                               anchor.features = integration_genes, 
                               verbose = FALSE)
integration_anchors <- FindIntegrationAnchors(object.list = meta_obj, 
                                              normalization.method = "SCT", 
                                              anchor.features = integration_genes, 
                                              verbose = FALSE)
meta <- IntegrateData(anchorset = integration_anchors, 
                      normalization.method = "SCT", 
                      k.weight = 50,
                      verbose = FALSE)
meta <- CellCycleScoring(meta, 
                         s.features = cc.genes.updated.2019$s.genes, 
                         g2m.features = cc.genes.updated.2019$g2m.genes)
DefaultAssay(meta) <- "integrated"

Preprocessing

Primary Tumors

We run PCA / UMAP, create the SNN graph representation of our cells, and cluster the cells using Louvain modularity optimization.

primary <- RunPCA(primary, 
                  npcs = 30, 
                  verbose = FALSE, 
                  seed.use = 629)
primary <- RunUMAP(primary, 
                   reduction = "pca", 
                   dims = 1:15, 
                   metric = "cosine",
                   min.dist = 0.1, 
                   n.components = 2, 
                   seed.use = 629, 
                   verbose = FALSE)
primary <- FindNeighbors(primary, 
                         reduction = "pca", 
                         dims = 1:15, 
                         k.param = 30, 
                         annoy.metric = "cosine", 
                         verbose = FALSE)
primary <- FindClusters(primary, 
                        resolution = 0.3, 
                        random.seed = 629,
                        verbose = FALSE)

Metastatic Tumors

Now we repeat the preprocessing on the metastatic tumors.

meta <- RunPCA(meta, 
               npcs = 30, 
               verbose = FALSE, 
               seed.use = 629)
meta <- RunUMAP(meta, 
                reduction = "pca", 
                dims = 1:30, 
                min.dist = 0.05,
                metric = "cosine", 
                n.components = 2, 
                seed.use = 629, 
                verbose = FALSE)
meta <- FindNeighbors(meta, 
                      reduction = "pca", 
                      dims = 1:30, 
                      annoy.metric = "cosine", 
                      verbose = FALSE)
meta <- FindClusters(meta, 
                     resolution = 0.15, 
                     verbose = FALSE, 
                     random.seed = 629)

Visualization

First we visualize the semi-supervised clustering we’ve generated using Louvain modularity optimization.

p0 <- DimPlot(primary) + 
      labs(subtitle = "Primary Tumor", x = "UMAP 1", y = "UMAP 2") + 
      scale_color_paletteer_d("ggthemes::Classic_20") + 
      theme_yehlab() + 
      theme(axis.title.x = element_text(hjust = 0, size = 10), 
            axis.title.y = element_text(hjust = 0, size = 10), 
            plot.subtitle = element_text(hjust = 0.5, size = 12)) + 
      guides(color = guide_legend(nrow = 2, byrow = TRUE, override.aes = list(size = 3)))
p1 <- DimPlot(meta, cols = paletteer_d("ggthemes::Classic_20")[8:12]) + 
      labs(subtitle = "Metastatic Tumor") + 
      theme_yehlab() + 
      theme(axis.title = element_blank(), 
            plot.subtitle = element_text(hjust = 0.5, size = 12)) + 
      guides(color = guide_legend(nrow = 1, byrow = TRUE, override.aes = list(size = 3)))
(p0 | p1) + plot_annotation(title = "Louvain Clustering", 
                            theme = theme(plot.title = element_text(hjust = 0.5)))

Next we check out how the samples are arranged in UMAP space. We see that there is next to no grouping by sample, which tells us that the data integration process worked correctly and the sample-related batch effect has been regressed out.

p2 <- DimPlot(primary, group.by = "sample") + 
      labs(x = "UMAP 1", y = "UMAP 2", subtitle = "Primary Tumor") + 
      scale_color_paletteer_d("miscpalettes::pastel") + 
      theme_yehlab() + 
      theme(axis.title = element_text(hjust = 0, size = 10), 
            plot.subtitle = element_text(hjust = 0.5), 
            legend.text = element_text(size = 10), 
            plot.title = element_blank()) + 
      guides(color = guide_legend(nrow = 2, byrow = TRUE, override.aes = list(size = 3)))
p3 <- DimPlot(meta, group.by = "sample") + 
      labs(subtitle = "Metastatic Tumor") + 
      scale_color_paletteer_d("miscpalettes::pastel") + 
      theme_yehlab() + 
      theme(axis.title = element_blank(), 
            plot.subtitle = element_text(hjust = 0.5), 
            legend.text = element_text(size = 10), 
            plot.title = element_blank()) + 
      guides(color = guide_legend(nrow = 2, byrow = TRUE, override.aes = list(size = 3)))
(p2 | p3) + plot_annotation(title = "Sample IDs", 
                            theme = theme(plot.caption = element_text(face = "italic"), 
                                          plot.title = element_text(hjust= 0.5)))

Analysis

Differential Expression

Primary Tumor

We use a Wilcox test for differential expression, and filter the results after the Bonferroni p-value correction at the \(q < 0.01\) level.

primary_markers <- FindAllMarkers(primary, 
                                  logfc.threshold = 2, 
                                  test.use = "wilcox", 
                                  only.pos = TRUE, 
                                  random.seed = 629, 
                                  verbose = FALSE) %>% 
                   filter(p_val_adj < 0.01)
primary_markers %>% 
  group_by(cluster) %>% 
  top_n(n = 5, wt = avg_log2FC) -> primary_markers_top5
p4 <- DotPlot(primary, 
              cols = c("grey70", "firebrick"), 
              features = primary_markers_top5$gene) + 
      labs(x = "Gene", 
           y = "Cluster", 
           color = "Mean Expression", 
           size = "Percent Expressed", 
           title = "Primary Tumor Marker Genes") + 
      theme(legend.position = "bottom", 
            axis.line = element_blank(), 
            legend.justification = "center", 
            plot.title = element_text(hjust = 0.5), 
            axis.title.y = element_text(angle = 360, vjust = 0.5), 
            panel.border = element_rect(fill = NA, size = 1, color = "black"), 
            axis.text.x = element_text(angle = 45, size = 8, vjust = 1, hjust = 1)) + 
      guides(color = guide_colorbar(title.position = "top", title.hjust = 0.5, barwidth = unit(5, "cm")), 
             size = guide_legend(title.position = "top", title.hjust = 0.5))

Clusters 0 and 1 are partially determined by LYZ & CXCL5, respectively, both of which are markers for Epithelial cells. Cluster 3 has CD68, which is a myeloid marker.

p4

Metastatic Tumor

We perform the same DE test on the metastatic tumor cells.

meta_markers <- FindAllMarkers(meta, 
                               logfc.threshold = 2, 
                               test.use = "wilcox", 
                               only.pos = TRUE, 
                               random.seed = 629, 
                               verbose = FALSE) %>% 
                filter(p_val_adj < 0.01)
meta_markers %>% 
  group_by(cluster) %>% 
  top_n(n = 5, wt = avg_log2FC) -> meta_markers_top5
p5 <- DotPlot(meta, 
              cols = c("grey70", "firebrick"), 
              features = meta_markers_top5$gene) + 
      labs(x = "Gene", 
           y = "Cluster", 
           color = "Mean Expression", 
           size = "Percent Expressed", 
           title = "Metastatic Tumor Marker Genes") + 
      theme(legend.position = "bottom", 
            axis.line = element_blank(), 
            legend.justification = "center", 
            plot.title = element_text(hjust = 0.5), 
            axis.title.y = element_text(angle = 360, vjust = 0.5), 
            panel.border = element_rect(fill = NA, size = 1, color = "black"), 
            axis.text.x = element_text(angle = 45, size = 8, vjust = 1, hjust = 1)) + 
      guides(color = guide_colorbar(title.position = "top", title.hjust = 0.5, barwidth = unit(5, "cm"), order = 2), 
             size = guide_legend(title.position = "top", title.hjust = 0.5, order = 1))

We can see C1QA, which is a myeloid marker, in cluster 3. Cluster 4 shows CD3D & NKG7; this indicates that the cluster is composed of T/NK cells. Cluster 2 has TOP2A, which is a DC marker.

p5

Cell Type Identification

SingleR: scRNA-seq Reference

SingleR uses Spearman correlation to compare scRNA-seq data to a labeled reference dataset. We’ll use the annotated cells from Baron et al (2016) in order to give broad cell types to our clusters. Note: these should not be considered ground truth, but can help guide our manual annotations. Also, this dataset can be loaded from the scRNAseq package, but I’ve created a version normalized using SCTransform instead og the default log-normalization, to better match the cells we’d like to annotate.

Primary Tumor

We read in the SCTransform-normalized reference dataset, and run SingleR.

sc_ref <- readRDS("/Volumes/labs/Home/Jen Jen Yeh Lab/Jack/scRNAseq/Seurat/single_cell_ref_normalized.Rds")
cell_preds <- SingleR(test = primary@assays$integrated@scale.data, 
                      ref = sc_ref, 
                      labels = sc_ref$label, 
                      clusters = primary$seurat_clusters, 
                      de.method = "wilcox")
primary[["SingleR_sc"]] <- cell_preds$labels[match(primary[[]][["seurat_clusters"]], rownames(cell_preds))]
primary[["SingleR_sc"]] <- case_when(primary[["SingleR_sc"]] == "acinar" ~ "Acinar", 
                                     primary[["SingleR_sc"]] == "activated_stellate" ~ "Activated Stellate", 
                                     primary[["SingleR_sc"]] == "ductal" ~ "Epithelial", 
                                     primary[["SingleR_sc"]] == "endothelial" ~ "Endothelial", 
                                     primary[["SingleR_sc"]] == "macrophage" ~ "Macrophage", 
                                     primary[["SingleR_sc"]] == "mast" ~ "Mast", 
                                     primary[["SingleR_sc"]] == "quiescent_stellate" ~ "Quiescent Stellate")
p6 <- DimPlot(primary, group.by = "SingleR_sc") + 
      labs(x = "UMAP 1", y = "UMAP 2", subtitle = "Primary Tumor") + 
      scale_color_paletteer_d("ggthemes::Classic_20") + 
      theme_yehlab() + 
      theme(legend.position = "right", 
            plot.title = element_blank(), 
            legend.text = element_text(size = 10), 
            plot.subtitle = element_text(), 
            axis.title = element_text(hjust = 0, size = 9)) + 
      guides(color = guide_legend(ncol = 1, override.aes = list(size = 3)))

Metastatic Tumor

We’ll do the same for the metastatic tumor cells.

cell_preds <- SingleR(test = meta@assays$integrated@scale.data, 
                      ref = sc_ref, 
                      labels = sc_ref$label, 
                      clusters = meta$seurat_clusters, 
                      de.method = "wilcox")
meta[["SingleR_sc"]] <- cell_preds$labels[match(meta[[]][["seurat_clusters"]], rownames(cell_preds))]
meta[["SingleR_sc"]] <- case_when(meta[["SingleR_sc"]] == "acinar" ~ "Acinar", 
                                  meta[["SingleR_sc"]] == "t_cell" ~ "T", 
                                  meta[["SingleR_sc"]] == "endothelial" ~ "Endothelial", 
                                  meta[["SingleR_sc"]] == "macrophage" ~ "Macrophage", 
                                  meta[["SingleR_sc"]] == "gamma" ~ "Gamma")
p7 <- DimPlot(meta, group.by = "SingleR_sc", cols = paletteer_d("ggthemes::Classic_20")[8:12]) + 
      labs(subtitle = "Metastatic Tumor") + 
      theme_yehlab() + 
      theme(legend.position = "right", 
            plot.title = element_blank(), 
            axis.title = element_blank(), 
            legend.text = element_text(size = 9), 
            plot.subtitle = element_text()) + 
      guides(color = guide_legend(ncol = 1, override.aes = list(size = 3)))

Let’s check out the results! I really doubt the Gamma label for the metastatic samples, as Gamma cells make up around 3-5% of all pancreatic islet cells. The T cells and Macrophage labels match up with the authors’ annotations. I would guess that the rest of the cells are split between normal ductal cells and PDAC cells.

As for the primary tumor cells, we see a Macrophage cluster and an Activated Stellate (read: CAF) cluster which matches the authors’ annotations. The acinar cells are interesting, as the authors noted that one of the tumor cluster was enriched for acinar marker genes. Most likely though, they are truly Epithelial cells which we’ll investigate further. Finally, the small Endothelial cluster roughly matches the size of the Endothelial cluster identified by the authors, though here it’s located next to the putative Epithelial cells instead of next to the CAFs as it was in the original publication.

(p7 / p6) + plot_annotation(title = "SingleR Annotations", subtitle = "scRNA-seq Reference", 
                            theme = theme(plot.title = element_text(size = 16, hjust = 0.5), 
                                          plot.subtitle = element_text(face = "italic", hjust = 0.5, size = 9)))

SingleR: Bulk RNA-seq Reference

We’ll perform the same procedure, this time using a bulk RNA-seq reference - data from the Human Primary Cell Atlas.

bulk_ref <- HumanPrimaryCellAtlasData()

Primary Tumor

We run SingleR again, this time using the bulk reference.

cell_preds <- SingleR(test = primary@assays$integrated@scale.data, 
                      ref = bulk_ref, 
                      labels = bulk_ref$label.main, 
                      clusters = primary$seurat_clusters, 
                      de.method = "wilcox")
primary[["SingleR_bulk"]] <- cell_preds$labels[match(primary[[]][["seurat_clusters"]], rownames(cell_preds))]
primary[["SingleR_bulk"]] <- case_when(primary[["SingleR_bulk"]] == "Epithelial_cells" ~ "Epithelial", 
                                       primary[["SingleR_bulk"]] == "Endothelial_cells" ~ "Endothelial", 
                                       primary[["SingleR_bulk"]] == "Fibroblasts" ~ "Fibroblast", 
                                       primary[["SingleR_bulk"]] == "Neutrophils" ~ "Neutrophil", 
                                       primary[["SingleR_bulk"]] == "Macrophage" ~ "Macrophage", 
                                       primary[["SingleR_bulk"]] == "BM & Prog." ~ "Bone Marrow Progenitor")
p8 <- DimPlot(primary, group.by = "SingleR_bulk") + 
      labs(x = "UMAP 1", y = "UMAP 2", subtitle = "Primary Tumor") + 
      scale_color_paletteer_d("ggthemes::Classic_20") + 
      theme_yehlab() + 
      theme(legend.position = "right", 
            plot.title = element_blank(), 
            legend.text = element_text(size = 9), 
            plot.subtitle = element_text(), 
            axis.title = element_text(hjust = 0, size = 9)) + 
      guides(color = guide_legend(ncol = 1, override.aes = list(size = 3)))

Metastatic Tumor

We’ll do the same for the metastatic tumor cells.

cell_preds <- SingleR(test = meta@assays$integrated@scale.data, 
                      ref = bulk_ref, 
                      labels = bulk_ref$label.main, 
                      clusters = meta$seurat_clusters, 
                      de.method = "wilcox")
meta[["SingleR_bulk"]] <- cell_preds$labels[match(meta[[]][["seurat_clusters"]], rownames(cell_preds))]
meta[["SingleR_bulk"]] <- case_when(meta[["SingleR_bulk"]] == "Epithelial_cells" ~ "Epithelial", 
                                  meta[["SingleR_bulk"]] == "Monocyte" ~ "Monocyte", 
                                  meta[["SingleR_bulk"]] == "T_cells" ~ "T", 
                                  meta[["SingleR_bulk"]] == "Neurons" ~ "Neuron")
p9 <- DimPlot(meta, group.by = "SingleR_bulk", cols = paletteer_d("ggthemes::Classic_20")[8:11]) + 
      labs(subtitle = "Metastatic Tumor") + 
      theme_yehlab() + 
      theme(legend.position = "right", 
            plot.title = element_blank(), 
            axis.title = element_blank(), 
            legend.text = element_text(size = 9), 
            plot.subtitle = element_text()) + 
      guides(color = guide_legend(ncol = 1, override.aes = list(size = 3)))

Let’s check out the results! For the primary tumor sample: we can be fairly certain that the large Epithelial cluster identity is correct, as we saw it in the single cell and bulk reference annotations. I think the Bone Marrow Progenitor cluster is most likely the Epithelial \(\rightarrow\) Mesenchymal cluster the authors annotated. The Fibroblast & Endothelial cluster identities also line up with what we saw in the single cell annotations. Lastly, I’m interested in the annotation of the Neutrophil cluster, as that’s something the authors did not annotate.

For the metastatic samples, we see again the small T & Macrophage clusters. We can be pretty sure that those are correct. I trust that the large Epithelial cluster is correct, though I’m puzzled as to why the final cluster was annotated as containing Neurons. The single cell reference defined that cluster as Endothelial. We’ll have to do some further investigation on that group of cells.

(p9 / p8) + plot_annotation(title = "SingleR Annotations", subtitle = "Bulk RNA-seq Reference", 
                            theme = theme(plot.title = element_text(size = 16, hjust = 0.5), 
                                          plot.subtitle = element_text(face = "italic", hjust = 0.5, size = 9)))

Pathway Analysis

Primary

We’ll use Dr. Xianlu Peng’s DECODER method in order to perform a pancreas-specific NMF version of pathway analysis, in which each cell is assigned compartment weights for pathways such as Immune, Classical PDAC, etc. The PDAC subtypes used here, Basal & Classical, are taken from Moffitt et al (2015).

sample_wts_unscaled <- Decon_single_sample("TCGA_RNAseq_PAAD", 
                                           primary@assays$integrated@data, 
                                           "geneSymbol")
sample_wts <- Norm_PDAC_weights(sample_wts_unscaled)
primary <- AddMetaData(primary, sample_wts$Immune, "immune")
primary <- AddMetaData(primary, sample_wts$bcRatio, "bc_ratio")
primary <- AddMetaData(primary, sample_wts$Endocrine, "endocrine")
primary <- AddMetaData(primary, sample_wts_unscaled[, 9], "basal")
primary <- AddMetaData(primary, sample_wts_unscaled[, 5], "classical")
primary <- AddMetaData(primary, sample_wts$NormalStroma, "norm_stroma")
primary <- AddMetaData(primary, sample_wts$ActivatedStroma, "act_stroma")

Now let’s generate some plots! We see that the Immune & Fibroblast scores correspond with what we saw in SingleR. Interestingly, it appears as though the PDAC scores are high in only one of the Epithelial clusters (Cluster 1). This leads me to believe that there are potential subclusters within the cluster, and that the other cluster is composed of normal, non-malignant Epithelial cells.

plots <- list()
vars <- c("immune", "endocrine", "basal", "classical", "norm_stroma", "act_stroma")
titles <- c("Immune", "Endocrine", "Basal PDAC", "Classical PDAC", "Normal Stroma", "Activated Stroma")
for (i in seq_along(vars)) {
  plots[[i]] <- FeaturePlot(primary, features = vars[i], cols = c("grey70", "firebrick")) + 
                labs(subtitle = titles[i]) + 
                theme_yehlab() + 
                theme(legend.position = "none", 
                      axis.title = element_blank(), 
                      plot.title = element_blank(), 
                      plot.title.position = "plot", 
                      plot.subtitle = element_text(hjust = 0.5, size = 12))
}
p10 <- ggarrange(plotlist = plots, ncol = 3, nrow = 2) %>% 
       annotate_figure(bottom = "UMAP 1", left = text_grob("UMAP 2", rot = 360), 
                       top = "DECODER Pathway Analysis (Primary Tumor)")
p10

Metastatic Tumor

We’ll repeat the DECODER analysis and plot generation for the metastatic cells.

sample_wts_unscaled <- Decon_single_sample("TCGA_RNAseq_PAAD", 
                                           meta@assays$integrated@data, 
                                           "geneSymbol")
sample_wts <- Norm_PDAC_weights(sample_wts_unscaled)
meta <- AddMetaData(meta, sample_wts$Immune, "immune")
meta <- AddMetaData(meta, sample_wts$bcRatio, "bc_ratio")
meta <- AddMetaData(meta, sample_wts$Endocrine, "endocrine")
meta <- AddMetaData(meta, sample_wts_unscaled[, 9], "basal")
meta <- AddMetaData(meta, sample_wts_unscaled[, 5], "classical")
meta <- AddMetaData(meta, sample_wts$NormalStroma, "norm_stroma")
meta <- AddMetaData(meta, sample_wts$ActivatedStroma, "act_stroma")

Now let’s generate some plots!

plots <- list()
vars <- c("immune", "endocrine", "basal", "classical", "norm_stroma", "act_stroma")
titles <- c("Immune", "Endocrine", "Basal PDAC", "Classical PDAC", "Normal Stroma", "Activated Stroma")
for (i in seq_along(vars)) {
  plots[[i]] <- FeaturePlot(meta, features = vars[i], cols = c("grey70", "firebrick")) + 
                labs(subtitle = titles[i]) + 
                theme_yehlab() + 
                theme(legend.position = "none", 
                      axis.title = element_blank(), 
                      plot.title = element_blank(), 
                      plot.title.position = "plot", 
                      plot.subtitle = element_text(hjust = 0.5, size = 12))
}

Despite some missing genes, we see that our T & Macrophage annotations match the Immune weights, and that the Basal & Classical PDAC scores are located in the putative Epithelial clusters. Specifically, the Basal PDAC weights are highest in the odd cluster that was annotated as Gamma / Neuron cells by SingleR. All three Epithelial clusters, though, do have either Basal or Classical PDAC cells in them.

p11 <- ggarrange(plotlist = plots, ncol = 3, nrow = 2) %>% 
       annotate_figure(bottom = "UMAP 1", left = text_grob("UMAP 2", rot = 360), 
                       top = "DECODER Pathway Analysis (Metastatic Tumor)")
p11

Assign Broad Cell Types

We have enough information to accurately assign broad cell types to our clusters.

Primary Tumor

primary$cell_type <- case_when(primary$seurat_clusters == 0 ~ "Epithelial", 
                               primary$seurat_clusters == 1 ~ "Epithelial", 
                               primary$seurat_clusters == 2 ~ "Fibroblast", 
                               primary$seurat_clusters == 3 ~ "Macrophage", 
                               primary$seurat_clusters == 4 ~ "Neutrophil", 
                               primary$seurat_clusters == 5 ~ "Mesenchymal Stem Cell", 
                               primary$seurat_clusters == 6 ~ "Fibroblast", 
                               primary$seurat_clusters == 7 ~ "Endothelial")

Metastatic Tumor

meta$cell_type <- case_when(meta$seurat_clusters == 0 ~ "Epithelial", 
                            meta$seurat_clusters == 1 ~ "Epithelial", 
                            meta$seurat_clusters == 2 ~ "Epithelial", 
                            meta$seurat_clusters == 3 ~ "Macrophage", 
                            meta$seurat_clusters == 4 ~ "T")

CNV Estimation

Next we’ll attempt to identify malignant cells using single-cell copy number variation estimation as implemented in the CONCISmat package. Details of the GMM methodology used can be found at the Diaz Lab’s GitHub repository.

Primary Tumor

chrom_regions <- read.table("/Volumes/labs/Home/Jen Jen Yeh Lab/Jack/scRNAseq/chrom_arm_positions.txt", 
                            sep = "\t", 
                            row.names = 1, 
                            header = TRUE)
gene_pos <- getGenePositions(rownames(primary))
cpm <- t(t(as.matrix(primary@assays$SCT@counts)) / colSums(as.matrix(primary@assays$SCT@counts))) * 10^5
cpm <- log2(cpm + 1)
norm_factor <- calcNormFactors(cpm)
cnv_est <- plotAll(mat = cpm, 
                   normFactor = norm_factor, 
                   regions = chrom_regions, 
                   gene_pos = gene_pos, 
                   fname = "../Data/Primary")
## [1] "Fitting GMM for chr1 0:122026459 iteration 1"
## number of iterations= 512 
## [1] "Fitting GMM for chr1 0:122026459 iteration 2"
## number of iterations= 405 
## [1] "Fitting GMM for chr1 0:122026459 iteration 3"
## number of iterations= 354 
## [1] "Fitting GMM for chr1 0:122026459 iteration 4"
## number of iterations= 499 
## [1] 44
## [1] "Fitting GMM for chr1 124932724:248956422 iteration 1"
## number of iterations= 438 
## [1] "Fitting GMM for chr1 124932724:248956422 iteration 2"
## number of iterations= 236 
## [1] "Fitting GMM for chr1 124932724:248956422 iteration 3"
## number of iterations= 331 
## [1] "Fitting GMM for chr1 124932724:248956422 iteration 4"
## number of iterations= 221 
## [1] 44
## [1] "Fitting GMM for chr2 0:92188145 iteration 1"
## WARNING! NOT CONVERGENT! 
## number of iterations= 1000 
## [1] "Fitting GMM for chr2 0:92188145 iteration 2"
## WARNING! NOT CONVERGENT! 
## number of iterations= 1000 
## [1] "Fitting GMM for chr2 0:92188145 iteration 3"
## WARNING! NOT CONVERGENT! 
## number of iterations= 1000 
## [1] "Fitting GMM for chr2 0:92188145 iteration 4"
## WARNING! NOT CONVERGENT! 
## number of iterations= 1000 
## [1] 44
## [1] "Fitting GMM for chr2 94090557:242193529 iteration 1"
## WARNING! NOT CONVERGENT! 
## number of iterations= 1000 
## [1] "Fitting GMM for chr2 94090557:242193529 iteration 2"
## WARNING! NOT CONVERGENT! 
## number of iterations= 1000 
## [1] "Fitting GMM for chr2 94090557:242193529 iteration 3"
## WARNING! NOT CONVERGENT! 
## number of iterations= 1000 
## [1] "Fitting GMM for chr2 94090557:242193529 iteration 4"
## WARNING! NOT CONVERGENT! 
## number of iterations= 1000 
## [1] 44
## [1] "Fitting GMM for chr3 0:90772458 iteration 1"
## WARNING! NOT CONVERGENT! 
## number of iterations= 1000 
## [1] "Fitting GMM for chr3 0:90772458 iteration 2"
## WARNING! NOT CONVERGENT! 
## number of iterations= 1000 
## [1] "Fitting GMM for chr3 0:90772458 iteration 3"
## WARNING! NOT CONVERGENT! 
## number of iterations= 1000 
## [1] "Fitting GMM for chr3 0:90772458 iteration 4"
## WARNING! NOT CONVERGENT! 
## number of iterations= 1000 
## [1] 44
## [1] "Fitting GMM for chr3 93655574:198295559 iteration 1"
## WARNING! NOT CONVERGENT! 
## number of iterations= 1000 
## [1] "Fitting GMM for chr3 93655574:198295559 iteration 2"
## WARNING! NOT CONVERGENT! 
## number of iterations= 1000 
## [1] "Fitting GMM for chr3 93655574:198295559 iteration 3"
## WARNING! NOT CONVERGENT! 
## number of iterations= 1000 
## [1] "Fitting GMM for chr3 93655574:198295559 iteration 4"
## WARNING! NOT CONVERGENT! 
## number of iterations= 1000 
## [1] 44
## [1] "Fitting GMM for chr4 51743951:190214555 iteration 1"
## number of iterations= 634 
## [1] "Fitting GMM for chr4 51743951:190214555 iteration 2"
## number of iterations= 371 
## [1] "Fitting GMM for chr4 51743951:190214555 iteration 3"
## WARNING! NOT CONVERGENT! 
## number of iterations= 1000 
## [1] "Fitting GMM for chr4 51743951:190214555 iteration 4"
## number of iterations= 943 
## [1] 44
## [1] "Fitting GMM for chr5 50059807:181538259 iteration 1"
## WARNING! NOT CONVERGENT! 
## number of iterations= 1000 
## [1] "Fitting GMM for chr5 50059807:181538259 iteration 2"
## WARNING! NOT CONVERGENT! 
## number of iterations= 1000 
## [1] "Fitting GMM for chr5 50059807:181538259 iteration 3"
## WARNING! NOT CONVERGENT! 
## number of iterations= 1000 
## [1] "Fitting GMM for chr5 50059807:181538259 iteration 4"
## WARNING! NOT CONVERGENT! 
## number of iterations= 1000 
## [1] 44
## [1] "Fitting GMM for chr7 61528020:159345973 iteration 1"
## WARNING! NOT CONVERGENT! 
## number of iterations= 1000 
## [1] "Fitting GMM for chr7 61528020:159345973 iteration 2"
## number of iterations= 947 
## [1] "Fitting GMM for chr7 61528020:159345973 iteration 3"
## number of iterations= 669 
## [1] "Fitting GMM for chr7 61528020:159345973 iteration 4"
## number of iterations= 573 
## [1] 44
## [1] "Fitting GMM for chr9 45518558:138394717 iteration 1"
## WARNING! NOT CONVERGENT! 
## number of iterations= 1000 
## [1] "Fitting GMM for chr9 45518558:138394717 iteration 2"
## WARNING! NOT CONVERGENT! 
## number of iterations= 1000 
## [1] "Fitting GMM for chr9 45518558:138394717 iteration 3"
## WARNING! NOT CONVERGENT! 
## number of iterations= 1000 
## [1] "Fitting GMM for chr9 45518558:138394717 iteration 4"
## WARNING! NOT CONVERGENT! 
## number of iterations= 1000 
## [1] 44
## [1] "Fitting GMM for chr10 41593521:133797422 iteration 1"
## number of iterations= 761 
## [1] "Fitting GMM for chr10 41593521:133797422 iteration 2"
## WARNING! NOT CONVERGENT! 
## number of iterations= 1000 
## [1] "Fitting GMM for chr10 41593521:133797422 iteration 3"
## number of iterations= 509 
## [1] "Fitting GMM for chr10 41593521:133797422 iteration 4"
## WARNING! NOT CONVERGENT! 
## number of iterations= 1000 
## [1] 44
## [1] "Fitting GMM for chr11 54425074:135086622 iteration 1"
## WARNING! NOT CONVERGENT! 
## number of iterations= 1000 
## [1] "Fitting GMM for chr11 54425074:135086622 iteration 2"
## WARNING! NOT CONVERGENT! 
## number of iterations= 1000 
## [1] "Fitting GMM for chr11 54425074:135086622 iteration 3"
## number of iterations= 848 
## [1] "Fitting GMM for chr11 54425074:135086622 iteration 4"
## WARNING! NOT CONVERGENT! 
## number of iterations= 1000 
## [1] 44
## [1] "Fitting GMM for chr12 37185252:133275309 iteration 1"
## number of iterations= 473 
## [1] "Fitting GMM for chr12 37185252:133275309 iteration 2"
## number of iterations= 674 
## [1] "Fitting GMM for chr12 37185252:133275309 iteration 3"
## number of iterations= 668 
## [1] "Fitting GMM for chr12 37185252:133275309 iteration 4"
## number of iterations= 561 
## [1] 44
## [1] "Fitting GMM for chr14 18173523:107043718 iteration 1"
## WARNING! NOT CONVERGENT! 
## number of iterations= 1000 
## [1] "Fitting GMM for chr14 18173523:107043718 iteration 2"
## WARNING! NOT CONVERGENT! 
## number of iterations= 1000 
## [1] "Fitting GMM for chr14 18173523:107043718 iteration 3"
## WARNING! NOT CONVERGENT! 
## number of iterations= 1000 
## [1] "Fitting GMM for chr14 18173523:107043718 iteration 4"
## WARNING! NOT CONVERGENT! 
## number of iterations= 1000 
## [1] 44
## [1] "Fitting GMM for chr15 19725254:101991189 iteration 1"
## WARNING! NOT CONVERGENT! 
## number of iterations= 1000 
## [1] "Fitting GMM for chr15 19725254:101991189 iteration 2"
## WARNING! NOT CONVERGENT! 
## number of iterations= 1000 
## [1] "Fitting GMM for chr15 19725254:101991189 iteration 3"
## WARNING! NOT CONVERGENT! 
## number of iterations= 1000 
## [1] "Fitting GMM for chr15 19725254:101991189 iteration 4"
## WARNING! NOT CONVERGENT! 
## number of iterations= 1000 
## [1] 44
## [1] "Fitting GMM for chr17 26566633:83257441 iteration 1"
## number of iterations= 675 
## [1] "Fitting GMM for chr17 26566633:83257441 iteration 2"
## number of iterations= 654 
## [1] "Fitting GMM for chr17 26566633:83257441 iteration 3"
## number of iterations= 538 
## [1] "Fitting GMM for chr17 26566633:83257441 iteration 4"
## number of iterations= 684 
## [1] 44
## [1] "Fitting GMM for chr19 0:24498980 iteration 1"
## number of iterations= 562 
## [1] "Fitting GMM for chr19 0:24498980 iteration 2"
## number of iterations= 633 
## [1] "Fitting GMM for chr19 0:24498980 iteration 3"
## number of iterations= 661 
## [1] "Fitting GMM for chr19 0:24498980 iteration 4"
## number of iterations= 505 
## [1] 44
## [1] "Fitting GMM for chr19 27190874:58617616 iteration 1"
## WARNING! NOT CONVERGENT! 
## number of iterations= 1000 
## [1] "Fitting GMM for chr19 27190874:58617616 iteration 2"
## WARNING! NOT CONVERGENT! 
## number of iterations= 1000 
## [1] "Fitting GMM for chr19 27190874:58617616 iteration 3"
## WARNING! NOT CONVERGENT! 
## number of iterations= 1000 
## [1] "Fitting GMM for chr19 27190874:58617616 iteration 4"
## WARNING! NOT CONVERGENT! 
## number of iterations= 1000 
## [1] 44
bic_table <- read.table("../Data/Primary_BIC_LR.txt", 
                        sep = "\t", 
                        row.names = 1, 
                        header = TRUE, 
                        check.names = FALSE)
cand_regions <- rownames(bic_table[bic_table$`BIC difference` > 200 & bic_table$`LRT adj. p-val` < .01, ])

We use \(k = 3\) clusters, as we assume that the cells can be divided into 1) PDAC cells, 2) CAF cells, and 3) non-malignant cells. We see that chromosomes 9q, 12q, and 17q have the most estimated CNVs.

hist1 <- plotHistogram(cnv_est[, cand_regions], 
                       cpm, 
                       clusters = 3, 
                       zscoreThreshold = 3, 
                       celltypes = primary$cell_type, 
                       patients = primary$sample)

normal_primary <- which(hist1 == 1)
primary@meta.data$CNV <- ifelse(rownames(primary@meta.data) %in% names(normal_primary), 
                                "Normal", "Malignant")
p12 <- DimPlot(primary, group.by = "CNV") + 
       labs(subtitle = "Primary Tumor") + 
       theme_yehlab() + 
       theme(legend.position = "none", 
             plot.title = element_blank(), 
             axis.title = element_blank(), 
             plot.subtitle = element_text(hjust = 0.5))

Metastatic Tumor

We repeat the process for the metastatic tumor samples.

gene_pos <- getGenePositions(rownames(meta))
cpm <- t(t(as.matrix(meta@assays$SCT@counts)) / colSums(as.matrix(meta@assays$SCT@counts))) * 10^5
cpm <- log2(cpm + 1)
norm_factor <- calcNormFactors(cpm)
cnv_est <- plotAll(mat = cpm, 
                   normFactor = norm_factor, 
                   regions = chrom_regions, 
                   gene_pos = gene_pos, 
                   fname = "../Data/Meta")
## [1] "Fitting GMM for chr1 0:122026459 iteration 1"
## number of iterations= 526 
## [1] "Fitting GMM for chr1 0:122026459 iteration 2"
## number of iterations= 504 
## [1] "Fitting GMM for chr1 0:122026459 iteration 3"
## number of iterations= 487 
## [1] "Fitting GMM for chr1 0:122026459 iteration 4"
## number of iterations= 283 
## [1] 44
## [1] "Fitting GMM for chr1 124932724:248956422 iteration 1"
## number of iterations= 413 
## [1] "Fitting GMM for chr1 124932724:248956422 iteration 2"
## number of iterations= 563 
## [1] "Fitting GMM for chr1 124932724:248956422 iteration 3"
## number of iterations= 304 
## [1] "Fitting GMM for chr1 124932724:248956422 iteration 4"
## number of iterations= 538 
## [1] 44
## [1] "Fitting GMM for chr2 0:92188145 iteration 1"
## number of iterations= 807 
## [1] "Fitting GMM for chr2 0:92188145 iteration 2"
## number of iterations= 660 
## [1] "Fitting GMM for chr2 0:92188145 iteration 3"
## number of iterations= 839 
## [1] "Fitting GMM for chr2 0:92188145 iteration 4"
## number of iterations= 651 
## [1] 44
## [1] "Fitting GMM for chr2 94090557:242193529 iteration 1"
## WARNING! NOT CONVERGENT! 
## number of iterations= 1000 
## [1] "Fitting GMM for chr2 94090557:242193529 iteration 2"
## WARNING! NOT CONVERGENT! 
## number of iterations= 1000 
## [1] "Fitting GMM for chr2 94090557:242193529 iteration 3"
## WARNING! NOT CONVERGENT! 
## number of iterations= 1000 
## [1] "Fitting GMM for chr2 94090557:242193529 iteration 4"
## WARNING! NOT CONVERGENT! 
## number of iterations= 1000 
## [1] 44
## [1] "Fitting GMM for chr3 93655574:198295559 iteration 1"
## WARNING! NOT CONVERGENT! 
## number of iterations= 1000 
## [1] "Fitting GMM for chr3 93655574:198295559 iteration 2"
## WARNING! NOT CONVERGENT! 
## number of iterations= 1000 
## [1] "Fitting GMM for chr3 93655574:198295559 iteration 3"
## WARNING! NOT CONVERGENT! 
## number of iterations= 1000 
## [1] "Fitting GMM for chr3 93655574:198295559 iteration 4"
## WARNING! NOT CONVERGENT! 
## number of iterations= 1000 
## [1] 44
## [1] "Fitting GMM for chr4 51743951:190214555 iteration 1"
## number of iterations= 959 
## [1] "Fitting GMM for chr4 51743951:190214555 iteration 2"
## WARNING! NOT CONVERGENT! 
## number of iterations= 1000 
## [1] "Fitting GMM for chr4 51743951:190214555 iteration 3"
## number of iterations= 997 
## [1] "Fitting GMM for chr4 51743951:190214555 iteration 4"
## number of iterations= 718 
## [1] 44
## [1] "Fitting GMM for chr5 50059807:181538259 iteration 1"
## WARNING! NOT CONVERGENT! 
## number of iterations= 1000 
## [1] "Fitting GMM for chr5 50059807:181538259 iteration 2"
## WARNING! NOT CONVERGENT! 
## number of iterations= 1000 
## [1] "Fitting GMM for chr5 50059807:181538259 iteration 3"
## number of iterations= 837 
## [1] "Fitting GMM for chr5 50059807:181538259 iteration 4"
## WARNING! NOT CONVERGENT! 
## number of iterations= 1000 
## [1] 44
## [1] "Fitting GMM for chr6 0:58553888 iteration 1"
## WARNING! NOT CONVERGENT! 
## number of iterations= 1000 
## [1] "Fitting GMM for chr6 0:58553888 iteration 2"
## WARNING! NOT CONVERGENT! 
## number of iterations= 1000 
## [1] "Fitting GMM for chr6 0:58553888 iteration 3"
## WARNING! NOT CONVERGENT! 
## number of iterations= 1000 
## [1] "Fitting GMM for chr6 0:58553888 iteration 4"
## WARNING! NOT CONVERGENT! 
## number of iterations= 1000 
## [1] 44
## [1] "Fitting GMM for chr7 61528020:159345973 iteration 1"
## number of iterations= 657 
## [1] "Fitting GMM for chr7 61528020:159345973 iteration 2"
## number of iterations= 359 
## [1] "Fitting GMM for chr7 61528020:159345973 iteration 3"
## WARNING! NOT CONVERGENT! 
## number of iterations= 1000 
## [1] "Fitting GMM for chr7 61528020:159345973 iteration 4"
## number of iterations= 715 
## [1] 44
## [1] "Fitting GMM for chr9 45518558:138394717 iteration 1"
## WARNING! NOT CONVERGENT! 
## number of iterations= 1000 
## [1] "Fitting GMM for chr9 45518558:138394717 iteration 2"
## WARNING! NOT CONVERGENT! 
## number of iterations= 1000 
## [1] "Fitting GMM for chr9 45518558:138394717 iteration 3"
## WARNING! NOT CONVERGENT! 
## number of iterations= 1000 
## [1] "Fitting GMM for chr9 45518558:138394717 iteration 4"
## WARNING! NOT CONVERGENT! 
## number of iterations= 1000 
## [1] 44
## [1] "Fitting GMM for chr10 41593521:133797422 iteration 1"
## number of iterations= 526 
## [1] "Fitting GMM for chr10 41593521:133797422 iteration 2"
## number of iterations= 274 
## [1] "Fitting GMM for chr10 41593521:133797422 iteration 3"
## number of iterations= 557 
## [1] "Fitting GMM for chr10 41593521:133797422 iteration 4"
## number of iterations= 372 
## [1] 44
## [1] "Fitting GMM for chr11 54425074:135086622 iteration 1"
## number of iterations= 651 
## [1] "Fitting GMM for chr11 54425074:135086622 iteration 2"
## number of iterations= 441 
## [1] "Fitting GMM for chr11 54425074:135086622 iteration 3"
## number of iterations= 788 
## [1] "Fitting GMM for chr11 54425074:135086622 iteration 4"
## number of iterations= 487 
## [1] 44
## [1] "Fitting GMM for chr12 37185252:133275309 iteration 1"
## number of iterations= 133 
## [1] "Fitting GMM for chr12 37185252:133275309 iteration 2"
## number of iterations= 275 
## [1] "Fitting GMM for chr12 37185252:133275309 iteration 3"
## number of iterations= 148 
## [1] "Fitting GMM for chr12 37185252:133275309 iteration 4"
## number of iterations= 286 
## [1] 44
## [1] "Fitting GMM for chr14 18173523:107043718 iteration 1"
## number of iterations= 788 
## [1] "Fitting GMM for chr14 18173523:107043718 iteration 2"
## number of iterations= 385 
## [1] "Fitting GMM for chr14 18173523:107043718 iteration 3"
## number of iterations= 531 
## [1] "Fitting GMM for chr14 18173523:107043718 iteration 4"
## number of iterations= 456 
## [1] 44
## [1] "Fitting GMM for chr15 19725254:101991189 iteration 1"
## number of iterations= 485 
## [1] "Fitting GMM for chr15 19725254:101991189 iteration 2"
## number of iterations= 351 
## [1] "Fitting GMM for chr15 19725254:101991189 iteration 3"
## number of iterations= 267 
## [1] "Fitting GMM for chr15 19725254:101991189 iteration 4"
## number of iterations= 322 
## [1] 44
## [1] "Fitting GMM for chr17 26566633:83257441 iteration 1"
## number of iterations= 350 
## [1] "Fitting GMM for chr17 26566633:83257441 iteration 2"
## number of iterations= 282 
## [1] "Fitting GMM for chr17 26566633:83257441 iteration 3"
## number of iterations= 379 
## [1] "Fitting GMM for chr17 26566633:83257441 iteration 4"
## number of iterations= 341 
## [1] 44
## [1] "Fitting GMM for chr19 0:24498980 iteration 1"
## WARNING! NOT CONVERGENT! 
## number of iterations= 1000 
## [1] "Fitting GMM for chr19 0:24498980 iteration 2"
## WARNING! NOT CONVERGENT! 
## number of iterations= 1000 
## [1] "Fitting GMM for chr19 0:24498980 iteration 3"
## WARNING! NOT CONVERGENT! 
## number of iterations= 1000 
## [1] "Fitting GMM for chr19 0:24498980 iteration 4"
## number of iterations= 220 
## [1] 44
## [1] "Fitting GMM for chr19 27190874:58617616 iteration 1"
## number of iterations= 423 
## [1] "Fitting GMM for chr19 27190874:58617616 iteration 2"
## number of iterations= 429 
## [1] "Fitting GMM for chr19 27190874:58617616 iteration 3"
## number of iterations= 470 
## [1] "Fitting GMM for chr19 27190874:58617616 iteration 4"
## WARNING! NOT CONVERGENT! 
## number of iterations= 1000 
## [1] 44
bic_table <- read.table("../Data/Meta_BIC_LR.txt", 
                        sep = "\t", 
                        row.names = 1, 
                        header = TRUE, 
                        check.names = FALSE)
cand_regions <- rownames(bic_table[bic_table$`BIC difference` > 200 & bic_table$`LRT adj. p-val` < .01, ])

We see that chromosomes 2p, 7q, 12q, and 15q have the most estimated CNVs for the metastatic samples.

hist2 <- plotHistogram(cnv_est[, cand_regions], 
                       cpm, 
                       clusters = 3, 
                       zscoreThreshold = 3, 
                       celltypes = meta$cell_type, 
                       patients = meta$sample)

normal_meta <- which(hist2 == 3)
meta@meta.data$CNV <- ifelse(rownames(meta@meta.data) %in% names(normal_meta), 
                             "Normal", "Malignant")
p13 <- DimPlot(meta, group.by = "CNV") + 
       labs(subtitle = "Metastatic Tumor") + 
       theme_yehlab() + 
       theme(legend.position = "none", 
             plot.title = element_blank(), 
             axis.title = element_blank(), 
             plot.subtitle = element_text(hjust = 0.5))

Let’s check out the results! We see that the cells estimated to be malignant are mostly located in the Epithelial clusters. This is what we’d expect from Pancreatic Ductal Adenocarcinoma. There are also a decent amount of malignant cells in the MSC cluster we identified. Notably, we don’t see a significant concentration of malignant cells in the Fibroblast cluster from the primary samples. This makes me doubt the authors’ annotation of those cells as Cancer Associated Fibroblasts (CAFs).

ggarrange(p12, p13, ncol = 2) %>% 
  annotate_figure(bottom = "UMAP 1", left = text_grob("UMAP 2", rot = 360), 
                  top = "Malignant Cells Estimated Through CONICSmat")

Reclustering Cells

Primary Tumor - Epithelial Cells

Lastly, we’ll use my method, SCISSORS, to determine whether separate Basal & Classical PDAC clusters exist in the PDAC cluster (Cluster 1) present in the primary tumor samples. The method re-processes the data and tests several clustering parameter sets in order to determine which parameter set fits the cells the best. This is done by maximizing the mean silhouette score for each cluster. Here we run the method and generate some plots of the results.

epi_reclust <- ReclusterCells(seurat.object = primary, 
                              which.clust = 1, 
                              n.HVG = 3000, 
                              n.PC = 15, 
                              k.vals = c(20, 30, 40), 
                              resolution.vals = c(.2, .3, .4), 
                              redo.embedding = TRUE, 
                              random.seed = 629)
## [1] "Reclustering cells in cluster 1 using k = 40 & resolution = 0.2; S = 0.349"
p14 <- DimPlot(epi_reclust) + 
       labs(subtitle = "SCISSORS - Cluster 1") + 
       theme_yehlab() + 
       theme(legend.position = "none", 
             plot.subtitle = element_text(hjust = 0.5), 
             axis.title = element_blank()) + 
       guides(color = guide_legend(ncol = 1, override.aes = list(size = 3)))
p15 <- FeaturePlot(epi_reclust, features = "classical", cols = c("grey90", "firebrick")) + 
       labs(subtitle = "Classical PDAC") + 
       theme_yehlab() + 
       theme(legend.position = "none",
             axis.title = element_blank(), 
             plot.title = element_blank(), 
             plot.subtitle = element_text(hjust = 0.5))
p16 <- FeaturePlot(epi_reclust, features = "basal", cols = c("grey90", "firebrick")) + 
       labs(subtitle = "Basal PDAC") + 
       theme_yehlab() + 
       theme(legend.position = "none",
             axis.title = element_blank(), 
             plot.title = element_blank(), 
             plot.subtitle = element_text(hjust = 0.5))
p0a <- p0 + 
       labs(subtitle = "Primary Tumor") + 
       theme(axis.title.y = element_blank(), 
             axis.title.x = element_blank(), 
             plot.title = element_blank(), 
             legend.position = "right", 
             plot.subtitle = element_text(hjust = 0.5)) + 
       guides(color = guide_legend(ncol = 1, override.aes = list(size = 3)))
p17 <- (p14 | p0a) / (p15 | p16)

We see that while it’s possible to find subclusters within Cluster 1, the “best” clustering does not separate out the Basal & Classical components of the PDAC cluster.

ggarrange(p17) %>% 
  annotate_figure(bottom = "UMAP 1", left = text_grob("UMAP 2", rot = 360))

Primary Tumor - Fibroblasts

Lastly, we saw in the DECODER results that one corner of the Fibroblast cluster in the primary tumor cells had high Endocrine weights. We’ll run SCISSORS on that cluster (Cluster 2) in order to see if subgroups can be extracted.

fibro_reclust <- ReclusterCells(seurat.object = primary, 
                                which.clust = 2, 
                                n.HVG = 3000, 
                                n.PC = 15, 
                                k.vals = c(10, 20, 30), 
                                resolution.vals = c(.2, .3, .4), 
                                redo.embedding = TRUE, 
                                random.seed = 629)
## [1] "Reclustering cells in cluster 2 using k = 30 & resolution = 0.2; S = 0.441"
fibro_markers <- FindAllMarkers(fibro_reclust, 
                                test.use = "wilcox", 
                                logfc.threshold = 2, 
                                only.pos = TRUE, 
                                verbose = FALSE, 
                                random.seed = 629)

We generate some plots of the results.

p18 <- DimPlot(fibro_reclust) + 
       labs(subtitle = "SCISSORS - Cluster 2") + 
       theme_yehlab() + 
       theme(legend.position = "none", 
             plot.subtitle = element_text(hjust = 0.5), 
             axis.title = element_blank()) + 
       guides(color = guide_legend(ncol = 1, override.aes = list(size = 3)))
p19 <- FeaturePlot(fibro_reclust, features = "act_stroma", cols = c("grey90", "firebrick")) + 
       labs(subtitle = "Activated Stroma") + 
       theme_yehlab() + 
       theme(legend.position = "none",
             axis.title = element_blank(), 
             plot.title = element_blank(), 
             plot.subtitle = element_text(hjust = 0.5))
p20 <- FeaturePlot(fibro_reclust, features = "endocrine", cols = c("grey90", "firebrick")) + 
       labs(subtitle = "Endocrine") + 
       theme_yehlab() + 
       theme(legend.position = "none",
             axis.title = element_blank(), 
             plot.title = element_blank(), 
             plot.subtitle = element_text(hjust = 0.5))
p21 <- (p18 | p0a) / (p19 | p20)

We see two clusters - one of which clearly corresponds to Activated Stroma / CAF cells, and the other of which seems so be composed of islet cells, as they have high endocrine pancreas scores.

ggarrange(p21) %>% 
  annotate_figure(bottom = "UMAP 1", left = text_grob("UMAP 2", rot = 360))

The top 5 markers for each cluster are a mixture of proto-oncogenes and stroma-related genes.

fibro_markers %>% 
  dplyr::select(cluster, gene, avg_log2FC, p_val_adj, pct.1, pct.2) %>% 
  group_by(cluster) %>% 
  slice_head(n = 5) %>% 
  kbl(booktabs = TRUE, col.names = c("Cluster", "Gene", "Mean Log2FC", 
                                     "Adj. P-value", "% Expressed 1", "% Expressed 2")) %>% 
  kable_minimal("hover", full_width = FALSE)
Cluster Gene Mean Log2FC Adj. P-value % Expressed 1 % Expressed 2
0 ITGB1 3.955181 0 0.963 0.564
0 TPM4 2.875344 0 0.961 0.569
0 SDC1 3.867239 0 0.809 0.157
0 MYL6 3.575856 0 0.972 0.795
0 IFI27 3.880991 0 0.770 0.182
1 JUNB 4.690781 0 0.986 0.594
1 JUN 5.476523 0 0.930 0.404
1 IER2 3.551652 0 0.977 0.504
1 FOSB 3.569275 0 0.861 0.287
1 FOS 3.576318 0 0.986 0.687

Assign Cell Types

Finally, we have enough information to accurately assign cell types to our clusters.

Primary Tumor

endocrine <- names(fibro_reclust$seurat_clusters[fibro_reclust$seurat_clusters == 1])
CAF <- names(fibro_reclust$seurat_clusters[fibro_reclust$seurat_clusters == 0])
primary$cell_type <- case_when(rownames(primary@meta.data) %in% endocrine ~ "Endocrine", 
                               rownames(primary@meta.data) %in% CAF ~ "CAF", 
                               primary$seurat_clusters == 1 ~ "PDAC", 
                               TRUE ~ primary$cell_type)
Idents(primary) <- "cell_type"
p22 <- DimPlot(primary, group.by = "cell_type") + 
       scale_color_paletteer_d("ggthemes::Classic_20") + 
       labs(subtitle = "Primary Tumor", x = "UMAP 1", y = "UMAP 2") + 
       theme_yehlab() + 
       theme(legend.position = "right", 
             plot.title = element_blank(), 
             legend.text = element_text(size = 10), 
             plot.subtitle = element_text(), 
             axis.title = element_text(hjust = 0, size = 9)) + 
       guides(color = guide_legend(ncol = 1, override.aes = list(size = 3)))

Metastatic Tumor

meta$cell_type <- case_when(meta$seurat_clusters == 0 ~ "PDAC", 
                            meta$seurat_clusters == 1 ~ "PDAC", 
                            meta$seurat_clusters == 2 ~ "PDAC", 
                            meta$seurat_clusters == 3 ~ "Macrophage", 
                            meta$seurat_clusters == 4 ~ "T")
Idents(meta) <- "cell_type"
p23 <- DimPlot(meta, group.by = "cell_type", cols = paletteer_d("ggthemes::Classic_20")[c(6, 9, 11)]) + 
       labs(subtitle = "Metastatic Tumor") + 
       theme_yehlab() + 
       theme(legend.position = "right", 
             plot.title = element_blank(), 
             axis.title = element_blank(), 
             legend.text = element_text(size = 9), 
             plot.subtitle = element_text()) + 
       guides(color = guide_legend(ncol = 1, override.aes = list(size = 3)))

Let’s check out the final results!

(p23 / p22) + plot_annotation(title = "Final Celltype Labels", 
                              theme = theme(plot.title = element_text(size = 16, hjust = 0.5)))

Differential Expression

Lastly, we’ll run differential expression tests again using our final cell labels this time instead of our cluster identities.

Primary Tumor

primary_markers_final <- FindAllMarkers(primary, 
                                        logfc.threshold = 2, 
                                        test.use = "wilcox", 
                                        verbose = FALSE, 
                                        only.pos = TRUE, 
                                        random.seed = 629)
primary_markers_final_top5 <- primary_markers_final %>% 
                              group_by(cluster) %>% 
                              slice_head(n = 5)
p24 <- DotPlot(primary, features = primary_markers_final_top5$gene, cols = c("grey90", "firebrick")) + 
       labs(x = "Gene", 
            y = "Cell", 
            color = "Mean Expression", 
            size = "Percent Expressed", 
            title = "Primary Tumor Marker Genes") + 
       theme(legend.position = "bottom", 
             axis.line = element_blank(), 
             legend.justification = "center", 
             plot.title = element_text(hjust = 0.5), 
             axis.title.y = element_blank(), 
             panel.border = element_rect(fill = NA, size = 1, color = "black"), 
             axis.text.x = element_text(angle = 45, size = 7.5, vjust = 1, hjust = 1), 
             axis.text.y = element_text(size = 8)) + 
       guides(color = guide_colorbar(title.position = "top", title.hjust = 0.5, barwidth = unit(5, "cm")), 
              size = guide_legend(title.position = "top", title.hjust = 0.5))

Our primary tumor clusters are fairly well-separated.

p24

Metastatic Tumor

meta_markers_final <- FindAllMarkers(meta, 
                                     logfc.threshold = 2, 
                                     test.use = "wilcox", 
                                     verbose = FALSE, 
                                     only.pos = TRUE, 
                                     random.seed = 629)
meta_markers_final_top7 <- meta_markers_final %>% 
                           group_by(cluster) %>% 
                           slice_head(n = 7)
p25 <- DotPlot(meta, features = unique(meta_markers_final_top7$gene), cols = c("grey90", "firebrick")) + 
       labs(x = "Gene", 
            y = "Cluster", 
            color = "Mean Expression", 
            size = "Percent Expressed", 
            title = "Metastatic Tumor Marker Genes") + 
       theme(legend.position = "bottom", 
             axis.line = element_blank(), 
             legend.justification = "center", 
             plot.title = element_text(hjust = 0.5), 
             axis.title.y = element_blank(), 
             panel.border = element_rect(fill = NA, size = 1, color = "black"), 
             axis.text.x = element_text(angle = 45, size = 8, vjust = 1, hjust = 1)) + 
       guides(color = guide_colorbar(title.position = "top", title.hjust = 0.5, barwidth = unit(5, "cm")), 
              size = guide_legend(title.position = "top", title.hjust = 0.5))

We also have good separation between celltypes for the metastatic samples.

p25

Save Results

saveRDS(primary, file = "~/Desktop/primary.Rds")
saveRDS(meta, file = "~/Desktop/meta.Rds")
LS0tCnRpdGxlOiAiUEhDIDY5MzcgKERhdGEgVmlzdWFsaXphdGlvbikgRmluYWwgUHJvamVjdCIKYXV0aG9yOiAiSmFjayBMZWFyeSIKZGF0ZTogImByIFN5cy5EYXRlKClgIgpvdXRwdXQ6CiAgaHRtbF9kb2N1bWVudDoKICAgIHRoZW1lOiBqb3VybmFsCiAgICBoaWdobGlnaHQ6IHRhbmdvCiAgICBkZl9wcmludDoga2FibGUKICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OgogICAgICBjb2xsYXBzZWQ6IGZhbHNlCiAgICAgIHNtb290aF9zY3JvbGw6IHRydWUKICAgIGNvZGVfZm9sZGluZzogc2hvdwogICAgY29kZV9kb3dubG9hZDogdHJ1ZQotLS0KCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUsIAogICAgICAgICAgICAgICAgICAgICAgbWVzc2FnZSA9IEZBTFNFLCAKICAgICAgICAgICAgICAgICAgICAgIHdhcm5pbmcgPSBGQUxTRSwgCiAgICAgICAgICAgICAgICAgICAgICBmaWcuYWxpZ24gPSAiY2VudGVyIikKb3B0aW9ucyhmdXR1cmUuZ2xvYmFscy5tYXhTaXplID0gMjAwMCAqIDEwMjReMikKc2V0LnNlZWQoMzEyKQpgYGAKCiMgSW50cm9kdWN0aW9uCgojIExpYmFyaWVzCgpgYGB7cn0KbGlicmFyeShkcGx5cikgICAgICAgIyB0aWR5IGRhdGEKbGlicmFyeShTZXVyYXQpICAgICAgIyBzY1JOQS1zZXEgaW5mcmFzdHJ1Y3R1cmUKbGlicmFyeShnZ3B1YnIpICAgICAgIyBwbG90IHV0aWxpdGllcwpsaWJyYXJ5KGNlbGxkZXgpICAgICAjIFJOQS1zZXEgcmVmZXJlbmNlIGRhdGFzZXRzCmxpYnJhcnkoZ2dwbG90MikgICAgICMgcGxvdHMKbGlicmFyeShTaW5nbGVSKSAgICAgIyBjZWxsIGFubm90YXRpb25zCmxpYnJhcnkoZGVjb2RlcnIpICAgICMgcGF0aHdheSBhbmFseXNpcyB0aHJvdWdoIE5NRgpsaWJyYXJ5KFNDSVNTT1JTKSAgICAjIHJlY2x1c3RlciBzY1JOQS1zZXEgZGF0YQpsaWJyYXJ5KENPTklDU21hdCkgICAjIGVzdGltYXRlIENOVnMKbGlicmFyeShwYXRjaHdvcmspICAgIyBhcnJhbmdlIHBsb3RzCmxpYnJhcnkoamFja2tuaWZlKSAgICMgbXkgcGFja2FnZQpsaWJyYXJ5KHBhbGV0dGVlcikgICAjIGFsbCB0aGUgcGFsZXR0ZXMKbGlicmFyeShrYWJsZUV4dHJhKSAgIyB0YWJsZSBzdHlsZXMKYGBgCgojIERhdGEKClRoZXJlIGFyZSBzZXZlcmFsIHNhbXBsZXMgaGVyZSwgc28gd2UnbGwgbmVlZCB0byBjcmVhdGUgYSBkaWN0aW9uYXJ5IG9mIHRoZSBzYW1wbGVuYW1lcyBhbmQgdGhlIHNhbXBsZSB0eXBlcy4gCgpgYGB7cn0KZGlyX25hbWVzIDwtIGMoIkdTTTQ2Nzk1MzIiLCAiR1NNNDY3OTUzMyIsICJHU000Njc5NTM0IiwgIkdTTTQ2Nzk1MzUiLCAiR1NNNDY3OTUzNiIsIAogICAgICAgICAgICAgICAiR1NNNDY3OTUzNyIsICJHU000Njc5NTM4IiwgIkdTTTQ2Nzk1MzkiLCAiR1NNNDY3OTU0MCIsICJHU000Njc5NTQxIiwgCiAgICAgICAgICAgICAgICJHU000Njc5NTQyIiwgIkdTTTQ2Nzk1NDMiLCAiR1NNNDY3OTU0NCIsICJHU000Njc5NTQ1IiwgIkdTTTQ2Nzk1NDYiLCAiR1NNNDY3OTU0NyIpCnNhbXBsZV9uYW1lcyA8LSBjKCJQMDEiLCAiUDAyIiwgIlAwMyIsICJQMDQiLCAiUDA1IiwgIlAwNiIsICJQMDciLCAiUDA4IiwgIlAwOSIsICJQMTAiLCAKICAgICAgICAgICAgICAgICAgIk1FVDAxIiwgIk1FVDAyIiwgIk1FVDAzIiwgIk1FVDA0IiwgIk1FVDA1IiwgIk1FVDA2IikKc2FtcGxlX3R5cGVzIDwtIGMocmVwKCJQcmltYXJ5IiwgMTApLCByZXAoIk1ldGFzdGF0aWMiLCA2KSkKc2FtcGxlX2RpY3QgPC0gZGF0YS5mcmFtZShEaXJlY3RvcnkgPSBkaXJfbmFtZXMsIAogICAgICAgICAgICAgICAgICAgICAgICAgIFNhbXBsZSA9IHNhbXBsZV9uYW1lcywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgVHlwZSA9IHNhbXBsZV90eXBlcykKc2FtcGxlX2RpY3QgJT4lIAogIGtibChib29rdGFicyA9IFRSVUUsIGNhcHRpb24gPSAiU2FtcGxlIERpY3Rpb25hcnkiKSAlPiUgCiAga2FibGVfbWluaW1hbCgiaG92ZXIiLCBodG1sX2ZvbnQgPSAiQXZlbmlyIiwgZnVsbF93aWR0aCA9IEZBTFNFKQpgYGAKCiMjIERhdGFzZXQgSW50ZWdyYXRpb24KCkZpcnN0LCB3ZSBsb29wIGFjcm9zcyB0aGUgc2FtcGxlcywgY3JlYXRlIGBTZXVyYXRgIG9iamVjdHMsIGFuZCBub3JtYWxpemUgZXhwcmVzc2lvbiBhbmQgaWRlbnRpZnkgdmFyaWFibGUgZ2VuZXMuIFRoZSBmaXJzdCAxMCBzYW1wbGVzIGFyZSBwcmltYXJ5IHR1bW9ycywgYW5kIHRoZSBuZXh0IDYgYXJlIG1ldGFzdGF0aWMuIAoKYGBge3J9Cm9ial9saXN0IDwtIGxpc3QoKQpmb3IgKGkgaW4gc2VxKHNhbXBsZV9kaWN0JERpcmVjdG9yeSkpIHsKICBmaWxlIDwtIHBhc3RlMCgifi9EZXNrdG9wL0RhdGEvTGluIGV0IGFsL0dTRTE1NDc3OF9SQVcvIiwgc2FtcGxlX2RpY3QkRGlyZWN0b3J5W2ldKQogIGNvdW50cyA8LSBSZWFkMTBYKGZpbGUpCiAgc2V1cmF0X29iaiA8LSBDcmVhdGVTZXVyYXRPYmplY3QoY291bnRzLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwcm9qZWN0ID0gc2FtcGxlX2RpY3QkU2FtcGxlW2ldLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtaW4uY2VsbHMgPSAzLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtaW4uZmVhdHVyZXMgPSAxMDAwKQogIHNldXJhdF9vYmpAbWV0YS5kYXRhJHNhbXBsZSA8LSBzYW1wbGVfZGljdCRTYW1wbGVbaV0KICBzZXVyYXRfb2JqW1sicGVyY2VudF9NVCJdXSA8LSBQZXJjZW50YWdlRmVhdHVyZVNldChzZXVyYXRfb2JqLCBwYXR0ZXJuID0gIl5NVC0iKQogIHNldXJhdF9vYmogPC0gU0NUcmFuc2Zvcm0oc2V1cmF0X29iaiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YXJpYWJsZS5mZWF0dXJlcy5uID0gNDAwMCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YXJzLnRvLnJlZ3Jlc3MgPSAicGVyY2VudF9NVCIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VlZC51c2UgPSA2MjksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgdmVyYm9zZSA9IEZBTFNFKQogIG9ial9saXN0W1tpXV0gPC0gc2V1cmF0X29iagp9CnByaW1hcnlfb2JqIDwtIG9ial9saXN0WzE6MTBdCm1ldGFfb2JqIDwtIG9ial9saXN0WzExOjE2XQpgYGAKCmBgYHtyLCBlY2hvPUZBTFNFfQpybShjb3VudHMsIG9ial9saXN0LCBzZXVyYXRfb2JqLCBkaXJfbmFtZXMsIGZpbGUsIHNhbXBsZV9uYW1lcywgc2FtcGxlX3R5cGVzKQpgYGAKCk5leHQgd2UgaW50ZWdyYXRlIGFsbCB0aGUgc2FtcGxlcyBpbnRvIG9uZSBgU2V1cmF0YCBvYmplY3QuIFdlJ2xsIHN0YXJ0IHdpdGggdGhlIHByaW1hcnkgc2FtcGxlcy4gCgpgYGB7cn0KaW50ZWdyYXRpb25fZ2VuZXMgPC0gU2VsZWN0SW50ZWdyYXRpb25GZWF0dXJlcyhvYmplY3QubGlzdCA9IHByaW1hcnlfb2JqLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuZmVhdHVyZXMgPSA0MDAwLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2ZXJib3NlID0gRkFMU0UpCnByaW1hcnlfb2JqIDwtIFByZXBTQ1RJbnRlZ3JhdGlvbihvYmplY3QubGlzdCA9IHByaW1hcnlfb2JqLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFuY2hvci5mZWF0dXJlcyA9IGludGVncmF0aW9uX2dlbmVzLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZlcmJvc2UgPSBGQUxTRSkKaW50ZWdyYXRpb25fYW5jaG9ycyA8LSBGaW5kSW50ZWdyYXRpb25BbmNob3JzKG9iamVjdC5saXN0ID0gcHJpbWFyeV9vYmosIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbm9ybWFsaXphdGlvbi5tZXRob2QgPSAiU0NUIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhbmNob3IuZmVhdHVyZXMgPSBpbnRlZ3JhdGlvbl9nZW5lcywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2ZXJib3NlID0gRkFMU0UpCnByaW1hcnkgPC0gSW50ZWdyYXRlRGF0YShhbmNob3JzZXQgPSBpbnRlZ3JhdGlvbl9hbmNob3JzLCAKICAgICAgICAgICAgICAgICAgICAgICAgIG5vcm1hbGl6YXRpb24ubWV0aG9kID0gIlNDVCIsIAogICAgICAgICAgICAgICAgICAgICAgICAgay53ZWlnaHQgPSA1MCwKICAgICAgICAgICAgICAgICAgICAgICAgIHZlcmJvc2UgPSBGQUxTRSkKcHJpbWFyeSA8LSBDZWxsQ3ljbGVTY29yaW5nKHByaW1hcnksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgcy5mZWF0dXJlcyA9IGNjLmdlbmVzLnVwZGF0ZWQuMjAxOSRzLmdlbmVzLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGcybS5mZWF0dXJlcyA9IGNjLmdlbmVzLnVwZGF0ZWQuMjAxOSRnMm0uZ2VuZXMpCkRlZmF1bHRBc3NheShwcmltYXJ5KSA8LSAiaW50ZWdyYXRlZCIKYGBgCgpXZSByZXBlYXQgdGhlIHByb2Nlc3MgZm9yIHRoZSBtZXRhc3RhdGljIHNhbXBsZXMuIAoKYGBge3J9CmludGVncmF0aW9uX2dlbmVzIDwtIFNlbGVjdEludGVncmF0aW9uRmVhdHVyZXMob2JqZWN0Lmxpc3QgPSBtZXRhX29iaiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmZlYXR1cmVzID0gNDAwMCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmVyYm9zZSA9IEZBTFNFKQptZXRhX29iaiA8LSBQcmVwU0NUSW50ZWdyYXRpb24ob2JqZWN0Lmxpc3QgPSBtZXRhX29iaiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhbmNob3IuZmVhdHVyZXMgPSBpbnRlZ3JhdGlvbl9nZW5lcywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2ZXJib3NlID0gRkFMU0UpCmludGVncmF0aW9uX2FuY2hvcnMgPC0gRmluZEludGVncmF0aW9uQW5jaG9ycyhvYmplY3QubGlzdCA9IG1ldGFfb2JqLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5vcm1hbGl6YXRpb24ubWV0aG9kID0gIlNDVCIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYW5jaG9yLmZlYXR1cmVzID0gaW50ZWdyYXRpb25fZ2VuZXMsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmVyYm9zZSA9IEZBTFNFKQptZXRhIDwtIEludGVncmF0ZURhdGEoYW5jaG9yc2V0ID0gaW50ZWdyYXRpb25fYW5jaG9ycywgCiAgICAgICAgICAgICAgICAgICAgICBub3JtYWxpemF0aW9uLm1ldGhvZCA9ICJTQ1QiLCAKICAgICAgICAgICAgICAgICAgICAgIGsud2VpZ2h0ID0gNTAsCiAgICAgICAgICAgICAgICAgICAgICB2ZXJib3NlID0gRkFMU0UpCm1ldGEgPC0gQ2VsbEN5Y2xlU2NvcmluZyhtZXRhLCAKICAgICAgICAgICAgICAgICAgICAgICAgIHMuZmVhdHVyZXMgPSBjYy5nZW5lcy51cGRhdGVkLjIwMTkkcy5nZW5lcywgCiAgICAgICAgICAgICAgICAgICAgICAgICBnMm0uZmVhdHVyZXMgPSBjYy5nZW5lcy51cGRhdGVkLjIwMTkkZzJtLmdlbmVzKQpEZWZhdWx0QXNzYXkobWV0YSkgPC0gImludGVncmF0ZWQiCmBgYAoKIyBQcmVwcm9jZXNzaW5nCgojIyBQcmltYXJ5IFR1bW9ycwoKV2UgcnVuIFBDQSAvIFVNQVAsIGNyZWF0ZSB0aGUgU05OIGdyYXBoIHJlcHJlc2VudGF0aW9uIG9mIG91ciBjZWxscywgYW5kIGNsdXN0ZXIgdGhlIGNlbGxzIHVzaW5nIExvdXZhaW4gbW9kdWxhcml0eSBvcHRpbWl6YXRpb24uIAoKYGBge3J9CnByaW1hcnkgPC0gUnVuUENBKHByaW1hcnksIAogICAgICAgICAgICAgICAgICBucGNzID0gMzAsIAogICAgICAgICAgICAgICAgICB2ZXJib3NlID0gRkFMU0UsIAogICAgICAgICAgICAgICAgICBzZWVkLnVzZSA9IDYyOSkKcHJpbWFyeSA8LSBSdW5VTUFQKHByaW1hcnksIAogICAgICAgICAgICAgICAgICAgcmVkdWN0aW9uID0gInBjYSIsIAogICAgICAgICAgICAgICAgICAgZGltcyA9IDE6MTUsIAogICAgICAgICAgICAgICAgICAgbWV0cmljID0gImNvc2luZSIsCiAgICAgICAgICAgICAgICAgICBtaW4uZGlzdCA9IDAuMSwgCiAgICAgICAgICAgICAgICAgICBuLmNvbXBvbmVudHMgPSAyLCAKICAgICAgICAgICAgICAgICAgIHNlZWQudXNlID0gNjI5LCAKICAgICAgICAgICAgICAgICAgIHZlcmJvc2UgPSBGQUxTRSkKcHJpbWFyeSA8LSBGaW5kTmVpZ2hib3JzKHByaW1hcnksIAogICAgICAgICAgICAgICAgICAgICAgICAgcmVkdWN0aW9uID0gInBjYSIsIAogICAgICAgICAgICAgICAgICAgICAgICAgZGltcyA9IDE6MTUsIAogICAgICAgICAgICAgICAgICAgICAgICAgay5wYXJhbSA9IDMwLCAKICAgICAgICAgICAgICAgICAgICAgICAgIGFubm95Lm1ldHJpYyA9ICJjb3NpbmUiLCAKICAgICAgICAgICAgICAgICAgICAgICAgIHZlcmJvc2UgPSBGQUxTRSkKcHJpbWFyeSA8LSBGaW5kQ2x1c3RlcnMocHJpbWFyeSwgCiAgICAgICAgICAgICAgICAgICAgICAgIHJlc29sdXRpb24gPSAwLjMsIAogICAgICAgICAgICAgICAgICAgICAgICByYW5kb20uc2VlZCA9IDYyOSwKICAgICAgICAgICAgICAgICAgICAgICAgdmVyYm9zZSA9IEZBTFNFKQpgYGAKCiMjIE1ldGFzdGF0aWMgVHVtb3JzCgpOb3cgd2UgcmVwZWF0IHRoZSBwcmVwcm9jZXNzaW5nIG9uIHRoZSBtZXRhc3RhdGljIHR1bW9ycy4gCgpgYGB7cn0KbWV0YSA8LSBSdW5QQ0EobWV0YSwgCiAgICAgICAgICAgICAgIG5wY3MgPSAzMCwgCiAgICAgICAgICAgICAgIHZlcmJvc2UgPSBGQUxTRSwgCiAgICAgICAgICAgICAgIHNlZWQudXNlID0gNjI5KQptZXRhIDwtIFJ1blVNQVAobWV0YSwgCiAgICAgICAgICAgICAgICByZWR1Y3Rpb24gPSAicGNhIiwgCiAgICAgICAgICAgICAgICBkaW1zID0gMTozMCwgCiAgICAgICAgICAgICAgICBtaW4uZGlzdCA9IDAuMDUsCiAgICAgICAgICAgICAgICBtZXRyaWMgPSAiY29zaW5lIiwgCiAgICAgICAgICAgICAgICBuLmNvbXBvbmVudHMgPSAyLCAKICAgICAgICAgICAgICAgIHNlZWQudXNlID0gNjI5LCAKICAgICAgICAgICAgICAgIHZlcmJvc2UgPSBGQUxTRSkKbWV0YSA8LSBGaW5kTmVpZ2hib3JzKG1ldGEsIAogICAgICAgICAgICAgICAgICAgICAgcmVkdWN0aW9uID0gInBjYSIsIAogICAgICAgICAgICAgICAgICAgICAgZGltcyA9IDE6MzAsIAogICAgICAgICAgICAgICAgICAgICAgYW5ub3kubWV0cmljID0gImNvc2luZSIsIAogICAgICAgICAgICAgICAgICAgICAgdmVyYm9zZSA9IEZBTFNFKQptZXRhIDwtIEZpbmRDbHVzdGVycyhtZXRhLCAKICAgICAgICAgICAgICAgICAgICAgcmVzb2x1dGlvbiA9IDAuMTUsIAogICAgICAgICAgICAgICAgICAgICB2ZXJib3NlID0gRkFMU0UsIAogICAgICAgICAgICAgICAgICAgICByYW5kb20uc2VlZCA9IDYyOSkKYGBgCgojIyBWaXN1YWxpemF0aW9uCgpGaXJzdCB3ZSB2aXN1YWxpemUgdGhlIHNlbWktc3VwZXJ2aXNlZCBjbHVzdGVyaW5nIHdlJ3ZlIGdlbmVyYXRlZCB1c2luZyBMb3V2YWluIG1vZHVsYXJpdHkgb3B0aW1pemF0aW9uLiAKCmBgYHtyfQpwMCA8LSBEaW1QbG90KHByaW1hcnkpICsgCiAgICAgIGxhYnMoc3VidGl0bGUgPSAiUHJpbWFyeSBUdW1vciIsIHggPSAiVU1BUCAxIiwgeSA9ICJVTUFQIDIiKSArIAogICAgICBzY2FsZV9jb2xvcl9wYWxldHRlZXJfZCgiZ2d0aGVtZXM6OkNsYXNzaWNfMjAiKSArIAogICAgICB0aGVtZV95ZWhsYWIoKSArIAogICAgICB0aGVtZShheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLCBzaXplID0gMTApLCAKICAgICAgICAgICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMCwgc2l6ZSA9IDEwKSwgCiAgICAgICAgICAgIHBsb3Quc3VidGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUsIHNpemUgPSAxMikpICsgCiAgICAgIGd1aWRlcyhjb2xvciA9IGd1aWRlX2xlZ2VuZChucm93ID0gMiwgYnlyb3cgPSBUUlVFLCBvdmVycmlkZS5hZXMgPSBsaXN0KHNpemUgPSAzKSkpCnAxIDwtIERpbVBsb3QobWV0YSwgY29scyA9IHBhbGV0dGVlcl9kKCJnZ3RoZW1lczo6Q2xhc3NpY18yMCIpWzg6MTJdKSArIAogICAgICBsYWJzKHN1YnRpdGxlID0gIk1ldGFzdGF0aWMgVHVtb3IiKSArIAogICAgICB0aGVtZV95ZWhsYWIoKSArIAogICAgICB0aGVtZShheGlzLnRpdGxlID0gZWxlbWVudF9ibGFuaygpLCAKICAgICAgICAgICAgcGxvdC5zdWJ0aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSwgc2l6ZSA9IDEyKSkgKyAKICAgICAgZ3VpZGVzKGNvbG9yID0gZ3VpZGVfbGVnZW5kKG5yb3cgPSAxLCBieXJvdyA9IFRSVUUsIG92ZXJyaWRlLmFlcyA9IGxpc3Qoc2l6ZSA9IDMpKSkKKHAwIHwgcDEpICsgcGxvdF9hbm5vdGF0aW9uKHRpdGxlID0gIkxvdXZhaW4gQ2x1c3RlcmluZyIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgdGhlbWUgPSB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkpCmBgYAoKTmV4dCB3ZSBjaGVjayBvdXQgaG93IHRoZSBzYW1wbGVzIGFyZSBhcnJhbmdlZCBpbiBVTUFQIHNwYWNlLiBXZSBzZWUgdGhhdCB0aGVyZSBpcyBuZXh0IHRvIG5vIGdyb3VwaW5nIGJ5IHNhbXBsZSwgd2hpY2ggdGVsbHMgdXMgdGhhdCB0aGUgZGF0YSBpbnRlZ3JhdGlvbiBwcm9jZXNzIHdvcmtlZCBjb3JyZWN0bHkgYW5kIHRoZSBzYW1wbGUtcmVsYXRlZCBiYXRjaCBlZmZlY3QgaGFzIGJlZW4gcmVncmVzc2VkIG91dC4gCgpgYGB7cn0KcDIgPC0gRGltUGxvdChwcmltYXJ5LCBncm91cC5ieSA9ICJzYW1wbGUiKSArIAogICAgICBsYWJzKHggPSAiVU1BUCAxIiwgeSA9ICJVTUFQIDIiLCBzdWJ0aXRsZSA9ICJQcmltYXJ5IFR1bW9yIikgKyAKICAgICAgc2NhbGVfY29sb3JfcGFsZXR0ZWVyX2QoIm1pc2NwYWxldHRlczo6cGFzdGVsIikgKyAKICAgICAgdGhlbWVfeWVobGFiKCkgKyAKICAgICAgdGhlbWUoYXhpcy50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAsIHNpemUgPSAxMCksIAogICAgICAgICAgICBwbG90LnN1YnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSwgCiAgICAgICAgICAgIGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCksIAogICAgICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF9ibGFuaygpKSArIAogICAgICBndWlkZXMoY29sb3IgPSBndWlkZV9sZWdlbmQobnJvdyA9IDIsIGJ5cm93ID0gVFJVRSwgb3ZlcnJpZGUuYWVzID0gbGlzdChzaXplID0gMykpKQpwMyA8LSBEaW1QbG90KG1ldGEsIGdyb3VwLmJ5ID0gInNhbXBsZSIpICsgCiAgICAgIGxhYnMoc3VidGl0bGUgPSAiTWV0YXN0YXRpYyBUdW1vciIpICsgCiAgICAgIHNjYWxlX2NvbG9yX3BhbGV0dGVlcl9kKCJtaXNjcGFsZXR0ZXM6OnBhc3RlbCIpICsgCiAgICAgIHRoZW1lX3llaGxhYigpICsgCiAgICAgIHRoZW1lKGF4aXMudGl0bGUgPSBlbGVtZW50X2JsYW5rKCksIAogICAgICAgICAgICBwbG90LnN1YnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSwgCiAgICAgICAgICAgIGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCksIAogICAgICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF9ibGFuaygpKSArIAogICAgICBndWlkZXMoY29sb3IgPSBndWlkZV9sZWdlbmQobnJvdyA9IDIsIGJ5cm93ID0gVFJVRSwgb3ZlcnJpZGUuYWVzID0gbGlzdChzaXplID0gMykpKQoocDIgfCBwMykgKyBwbG90X2Fubm90YXRpb24odGl0bGUgPSAiU2FtcGxlIElEcyIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgdGhlbWUgPSB0aGVtZShwbG90LmNhcHRpb24gPSBlbGVtZW50X3RleHQoZmFjZSA9ICJpdGFsaWMiKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3Q9IDAuNSkpKQpgYGAKCiMgQW5hbHlzaXMgCgojIyBEaWZmZXJlbnRpYWwgRXhwcmVzc2lvbgoKIyMjIFByaW1hcnkgVHVtb3IgCgpXZSB1c2UgYSBXaWxjb3ggdGVzdCBmb3IgZGlmZmVyZW50aWFsIGV4cHJlc3Npb24sIGFuZCBmaWx0ZXIgdGhlIHJlc3VsdHMgYWZ0ZXIgdGhlIEJvbmZlcnJvbmkgKnAqLXZhbHVlIGNvcnJlY3Rpb24gYXQgdGhlICRxIDwgMC4wMSQgbGV2ZWwuIAoKYGBge3J9CnByaW1hcnlfbWFya2VycyA8LSBGaW5kQWxsTWFya2VycyhwcmltYXJ5LCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxvZ2ZjLnRocmVzaG9sZCA9IDIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdGVzdC51c2UgPSAid2lsY294IiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBvbmx5LnBvcyA9IFRSVUUsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmFuZG9tLnNlZWQgPSA2MjksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmVyYm9zZSA9IEZBTFNFKSAlPiUgCiAgICAgICAgICAgICAgICAgICBmaWx0ZXIocF92YWxfYWRqIDwgMC4wMSkKcHJpbWFyeV9tYXJrZXJzICU+JSAKICBncm91cF9ieShjbHVzdGVyKSAlPiUgCiAgdG9wX24obiA9IDUsIHd0ID0gYXZnX2xvZzJGQykgLT4gcHJpbWFyeV9tYXJrZXJzX3RvcDUKcDQgPC0gRG90UGxvdChwcmltYXJ5LCAKICAgICAgICAgICAgICBjb2xzID0gYygiZ3JleTcwIiwgImZpcmVicmljayIpLCAKICAgICAgICAgICAgICBmZWF0dXJlcyA9IHByaW1hcnlfbWFya2Vyc190b3A1JGdlbmUpICsgCiAgICAgIGxhYnMoeCA9ICJHZW5lIiwgCiAgICAgICAgICAgeSA9ICJDbHVzdGVyIiwgCiAgICAgICAgICAgY29sb3IgPSAiTWVhbiBFeHByZXNzaW9uIiwgCiAgICAgICAgICAgc2l6ZSA9ICJQZXJjZW50IEV4cHJlc3NlZCIsIAogICAgICAgICAgIHRpdGxlID0gIlByaW1hcnkgVHVtb3IgTWFya2VyIEdlbmVzIikgKyAKICAgICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIsIAogICAgICAgICAgICBheGlzLmxpbmUgPSBlbGVtZW50X2JsYW5rKCksIAogICAgICAgICAgICBsZWdlbmQuanVzdGlmaWNhdGlvbiA9ICJjZW50ZXIiLCAKICAgICAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSksIAogICAgICAgICAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQoYW5nbGUgPSAzNjAsIHZqdXN0ID0gMC41KSwgCiAgICAgICAgICAgIHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfcmVjdChmaWxsID0gTkEsIHNpemUgPSAxLCBjb2xvciA9ICJibGFjayIpLCAKICAgICAgICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgc2l6ZSA9IDgsIHZqdXN0ID0gMSwgaGp1c3QgPSAxKSkgKyAKICAgICAgZ3VpZGVzKGNvbG9yID0gZ3VpZGVfY29sb3JiYXIodGl0bGUucG9zaXRpb24gPSAidG9wIiwgdGl0bGUuaGp1c3QgPSAwLjUsIGJhcndpZHRoID0gdW5pdCg1LCAiY20iKSksIAogICAgICAgICAgICAgc2l6ZSA9IGd1aWRlX2xlZ2VuZCh0aXRsZS5wb3NpdGlvbiA9ICJ0b3AiLCB0aXRsZS5oanVzdCA9IDAuNSkpCmBgYAoKQ2x1c3RlcnMgMCBhbmQgMSBhcmUgcGFydGlhbGx5IGRldGVybWluZWQgYnkgTFlaICYgQ1hDTDUsIHJlc3BlY3RpdmVseSwgYm90aCBvZiB3aGljaCBhcmUgbWFya2VycyBmb3IgRXBpdGhlbGlhbCBjZWxscy4gQ2x1c3RlciAzIGhhcyBDRDY4LCB3aGljaCBpcyBhIG15ZWxvaWQgbWFya2VyLiAKCmBgYHtyfQpwNApgYGAKCiMjIyBNZXRhc3RhdGljIFR1bW9yCgpXZSBwZXJmb3JtIHRoZSBzYW1lIERFIHRlc3Qgb24gdGhlIG1ldGFzdGF0aWMgdHVtb3IgY2VsbHMuIAoKYGBge3J9Cm1ldGFfbWFya2VycyA8LSBGaW5kQWxsTWFya2VycyhtZXRhLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxvZ2ZjLnRocmVzaG9sZCA9IDIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdGVzdC51c2UgPSAid2lsY294IiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBvbmx5LnBvcyA9IFRSVUUsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmFuZG9tLnNlZWQgPSA2MjksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmVyYm9zZSA9IEZBTFNFKSAlPiUgCiAgICAgICAgICAgICAgICBmaWx0ZXIocF92YWxfYWRqIDwgMC4wMSkKbWV0YV9tYXJrZXJzICU+JSAKICBncm91cF9ieShjbHVzdGVyKSAlPiUgCiAgdG9wX24obiA9IDUsIHd0ID0gYXZnX2xvZzJGQykgLT4gbWV0YV9tYXJrZXJzX3RvcDUKcDUgPC0gRG90UGxvdChtZXRhLCAKICAgICAgICAgICAgICBjb2xzID0gYygiZ3JleTcwIiwgImZpcmVicmljayIpLCAKICAgICAgICAgICAgICBmZWF0dXJlcyA9IG1ldGFfbWFya2Vyc190b3A1JGdlbmUpICsgCiAgICAgIGxhYnMoeCA9ICJHZW5lIiwgCiAgICAgICAgICAgeSA9ICJDbHVzdGVyIiwgCiAgICAgICAgICAgY29sb3IgPSAiTWVhbiBFeHByZXNzaW9uIiwgCiAgICAgICAgICAgc2l6ZSA9ICJQZXJjZW50IEV4cHJlc3NlZCIsIAogICAgICAgICAgIHRpdGxlID0gIk1ldGFzdGF0aWMgVHVtb3IgTWFya2VyIEdlbmVzIikgKyAKICAgICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIsIAogICAgICAgICAgICBheGlzLmxpbmUgPSBlbGVtZW50X2JsYW5rKCksIAogICAgICAgICAgICBsZWdlbmQuanVzdGlmaWNhdGlvbiA9ICJjZW50ZXIiLCAKICAgICAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSksIAogICAgICAgICAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQoYW5nbGUgPSAzNjAsIHZqdXN0ID0gMC41KSwgCiAgICAgICAgICAgIHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfcmVjdChmaWxsID0gTkEsIHNpemUgPSAxLCBjb2xvciA9ICJibGFjayIpLCAKICAgICAgICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgc2l6ZSA9IDgsIHZqdXN0ID0gMSwgaGp1c3QgPSAxKSkgKyAKICAgICAgZ3VpZGVzKGNvbG9yID0gZ3VpZGVfY29sb3JiYXIodGl0bGUucG9zaXRpb24gPSAidG9wIiwgdGl0bGUuaGp1c3QgPSAwLjUsIGJhcndpZHRoID0gdW5pdCg1LCAiY20iKSwgb3JkZXIgPSAyKSwgCiAgICAgICAgICAgICBzaXplID0gZ3VpZGVfbGVnZW5kKHRpdGxlLnBvc2l0aW9uID0gInRvcCIsIHRpdGxlLmhqdXN0ID0gMC41LCBvcmRlciA9IDEpKQpgYGAKCldlIGNhbiBzZWUgQzFRQSwgd2hpY2ggaXMgYSBteWVsb2lkIG1hcmtlciwgaW4gY2x1c3RlciAzLiBDbHVzdGVyIDQgc2hvd3MgQ0QzRCAmIE5LRzc7IHRoaXMgaW5kaWNhdGVzIHRoYXQgdGhlIGNsdXN0ZXIgaXMgY29tcG9zZWQgb2YgVC9OSyBjZWxscy4gQ2x1c3RlciAyIGhhcyBUT1AyQSwgd2hpY2ggaXMgYSBEQyBtYXJrZXIuIAoKYGBge3J9CnA1CmBgYAoKIyMgQ2VsbCBUeXBlIElkZW50aWZpY2F0aW9uIAoKIyMjIFNpbmdsZVI6IHNjUk5BLXNlcSBSZWZlcmVuY2UKCltgU2luZ2xlUmBdKGh0dHBzOi8vd3d3Lm5hdHVyZS5jb20vYXJ0aWNsZXMvczQxNTkwLTAxOC0wMjc2LXkpIHVzZXMgU3BlYXJtYW4gY29ycmVsYXRpb24gdG8gY29tcGFyZSBzY1JOQS1zZXEgZGF0YSB0byBhIGxhYmVsZWQgcmVmZXJlbmNlIGRhdGFzZXQuIFdlJ2xsIHVzZSB0aGUgYW5ub3RhdGVkIGNlbGxzIGZyb20gW0Jhcm9uICpldCBhbCogKDIwMTYpXShodHRwczovL3B1Ym1lZC5uY2JpLm5sbS5uaWguZ292LzI3NjY3MzY1LykgaW4gb3JkZXIgdG8gZ2l2ZSBicm9hZCBjZWxsIHR5cGVzIHRvIG91ciBjbHVzdGVycy4gTm90ZTogdGhlc2Ugc2hvdWxkIG5vdCBiZSBjb25zaWRlcmVkIGdyb3VuZCB0cnV0aCwgYnV0IGNhbiBoZWxwIGd1aWRlIG91ciBtYW51YWwgYW5ub3RhdGlvbnMuIEFsc28sIHRoaXMgZGF0YXNldCBjYW4gYmUgbG9hZGVkIGZyb20gdGhlIGBzY1JOQXNlcWAgcGFja2FnZSwgYnV0IEkndmUgY3JlYXRlZCBhIHZlcnNpb24gbm9ybWFsaXplZCB1c2luZyBgU0NUcmFuc2Zvcm1gIGluc3RlYWQgb2cgdGhlIGRlZmF1bHQgbG9nLW5vcm1hbGl6YXRpb24sIHRvIGJldHRlciBtYXRjaCB0aGUgY2VsbHMgd2UnZCBsaWtlIHRvIGFubm90YXRlLgoKIyMjIyBQcmltYXJ5IFR1bW9yCgpXZSByZWFkIGluIHRoZSBgU0NUcmFuc2Zvcm1gLW5vcm1hbGl6ZWQgcmVmZXJlbmNlIGRhdGFzZXQsIGFuZCBydW4gYFNpbmdsZVJgLiAKCmBgYHtyfQpzY19yZWYgPC0gcmVhZFJEUygiL1ZvbHVtZXMvbGFicy9Ib21lL0plbiBKZW4gWWVoIExhYi9KYWNrL3NjUk5Bc2VxL1NldXJhdC9zaW5nbGVfY2VsbF9yZWZfbm9ybWFsaXplZC5SZHMiKQpjZWxsX3ByZWRzIDwtIFNpbmdsZVIodGVzdCA9IHByaW1hcnlAYXNzYXlzJGludGVncmF0ZWRAc2NhbGUuZGF0YSwgCiAgICAgICAgICAgICAgICAgICAgICByZWYgPSBzY19yZWYsIAogICAgICAgICAgICAgICAgICAgICAgbGFiZWxzID0gc2NfcmVmJGxhYmVsLCAKICAgICAgICAgICAgICAgICAgICAgIGNsdXN0ZXJzID0gcHJpbWFyeSRzZXVyYXRfY2x1c3RlcnMsIAogICAgICAgICAgICAgICAgICAgICAgZGUubWV0aG9kID0gIndpbGNveCIpCnByaW1hcnlbWyJTaW5nbGVSX3NjIl1dIDwtIGNlbGxfcHJlZHMkbGFiZWxzW21hdGNoKHByaW1hcnlbW11dW1sic2V1cmF0X2NsdXN0ZXJzIl1dLCByb3duYW1lcyhjZWxsX3ByZWRzKSldCnByaW1hcnlbWyJTaW5nbGVSX3NjIl1dIDwtIGNhc2Vfd2hlbihwcmltYXJ5W1siU2luZ2xlUl9zYyJdXSA9PSAiYWNpbmFyIiB+ICJBY2luYXIiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHByaW1hcnlbWyJTaW5nbGVSX3NjIl1dID09ICJhY3RpdmF0ZWRfc3RlbGxhdGUiIH4gIkFjdGl2YXRlZCBTdGVsbGF0ZSIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcHJpbWFyeVtbIlNpbmdsZVJfc2MiXV0gPT0gImR1Y3RhbCIgfiAiRXBpdGhlbGlhbCIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcHJpbWFyeVtbIlNpbmdsZVJfc2MiXV0gPT0gImVuZG90aGVsaWFsIiB+ICJFbmRvdGhlbGlhbCIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcHJpbWFyeVtbIlNpbmdsZVJfc2MiXV0gPT0gIm1hY3JvcGhhZ2UiIH4gIk1hY3JvcGhhZ2UiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHByaW1hcnlbWyJTaW5nbGVSX3NjIl1dID09ICJtYXN0IiB+ICJNYXN0IiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwcmltYXJ5W1siU2luZ2xlUl9zYyJdXSA9PSAicXVpZXNjZW50X3N0ZWxsYXRlIiB+ICJRdWllc2NlbnQgU3RlbGxhdGUiKQpwNiA8LSBEaW1QbG90KHByaW1hcnksIGdyb3VwLmJ5ID0gIlNpbmdsZVJfc2MiKSArIAogICAgICBsYWJzKHggPSAiVU1BUCAxIiwgeSA9ICJVTUFQIDIiLCBzdWJ0aXRsZSA9ICJQcmltYXJ5IFR1bW9yIikgKyAKICAgICAgc2NhbGVfY29sb3JfcGFsZXR0ZWVyX2QoImdndGhlbWVzOjpDbGFzc2ljXzIwIikgKyAKICAgICAgdGhlbWVfeWVobGFiKCkgKyAKICAgICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInJpZ2h0IiwgCiAgICAgICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X2JsYW5rKCksIAogICAgICAgICAgICBsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApLCAKICAgICAgICAgICAgcGxvdC5zdWJ0aXRsZSA9IGVsZW1lbnRfdGV4dCgpLCAKICAgICAgICAgICAgYXhpcy50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAsIHNpemUgPSA5KSkgKyAKICAgICAgZ3VpZGVzKGNvbG9yID0gZ3VpZGVfbGVnZW5kKG5jb2wgPSAxLCBvdmVycmlkZS5hZXMgPSBsaXN0KHNpemUgPSAzKSkpCmBgYAoKIyMjIyBNZXRhc3RhdGljIFR1bW9yCgpXZSdsbCBkbyB0aGUgc2FtZSBmb3IgdGhlIG1ldGFzdGF0aWMgdHVtb3IgY2VsbHMuIAoKYGBge3J9CmNlbGxfcHJlZHMgPC0gU2luZ2xlUih0ZXN0ID0gbWV0YUBhc3NheXMkaW50ZWdyYXRlZEBzY2FsZS5kYXRhLCAKICAgICAgICAgICAgICAgICAgICAgIHJlZiA9IHNjX3JlZiwgCiAgICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBzY19yZWYkbGFiZWwsIAogICAgICAgICAgICAgICAgICAgICAgY2x1c3RlcnMgPSBtZXRhJHNldXJhdF9jbHVzdGVycywgCiAgICAgICAgICAgICAgICAgICAgICBkZS5tZXRob2QgPSAid2lsY294IikKbWV0YVtbIlNpbmdsZVJfc2MiXV0gPC0gY2VsbF9wcmVkcyRsYWJlbHNbbWF0Y2gobWV0YVtbXV1bWyJzZXVyYXRfY2x1c3RlcnMiXV0sIHJvd25hbWVzKGNlbGxfcHJlZHMpKV0KbWV0YVtbIlNpbmdsZVJfc2MiXV0gPC0gY2FzZV93aGVuKG1ldGFbWyJTaW5nbGVSX3NjIl1dID09ICJhY2luYXIiIH4gIkFjaW5hciIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWV0YVtbIlNpbmdsZVJfc2MiXV0gPT0gInRfY2VsbCIgfiAiVCIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWV0YVtbIlNpbmdsZVJfc2MiXV0gPT0gImVuZG90aGVsaWFsIiB+ICJFbmRvdGhlbGlhbCIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWV0YVtbIlNpbmdsZVJfc2MiXV0gPT0gIm1hY3JvcGhhZ2UiIH4gIk1hY3JvcGhhZ2UiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1ldGFbWyJTaW5nbGVSX3NjIl1dID09ICJnYW1tYSIgfiAiR2FtbWEiKQpwNyA8LSBEaW1QbG90KG1ldGEsIGdyb3VwLmJ5ID0gIlNpbmdsZVJfc2MiLCBjb2xzID0gcGFsZXR0ZWVyX2QoImdndGhlbWVzOjpDbGFzc2ljXzIwIilbODoxMl0pICsgCiAgICAgIGxhYnMoc3VidGl0bGUgPSAiTWV0YXN0YXRpYyBUdW1vciIpICsgCiAgICAgIHRoZW1lX3llaGxhYigpICsgCiAgICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJyaWdodCIsIAogICAgICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF9ibGFuaygpLCAKICAgICAgICAgICAgYXhpcy50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSwgCiAgICAgICAgICAgIGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSA5KSwgCiAgICAgICAgICAgIHBsb3Quc3VidGl0bGUgPSBlbGVtZW50X3RleHQoKSkgKyAKICAgICAgZ3VpZGVzKGNvbG9yID0gZ3VpZGVfbGVnZW5kKG5jb2wgPSAxLCBvdmVycmlkZS5hZXMgPSBsaXN0KHNpemUgPSAzKSkpCmBgYAoKTGV0J3MgY2hlY2sgb3V0IHRoZSByZXN1bHRzISBJIHJlYWxseSBkb3VidCB0aGUgR2FtbWEgbGFiZWwgZm9yIHRoZSBtZXRhc3RhdGljIHNhbXBsZXMsIGFzIEdhbW1hIGNlbGxzIG1ha2UgdXAgYXJvdW5kIDMtNSUgb2YgYWxsIHBhbmNyZWF0aWMgaXNsZXQgY2VsbHMuIFRoZSBUIGNlbGxzIGFuZCBNYWNyb3BoYWdlIGxhYmVscyBtYXRjaCB1cCB3aXRoIHRoZSBhdXRob3JzJyBhbm5vdGF0aW9ucy4gSSB3b3VsZCBndWVzcyB0aGF0IHRoZSByZXN0IG9mIHRoZSBjZWxscyBhcmUgc3BsaXQgYmV0d2VlbiBub3JtYWwgZHVjdGFsIGNlbGxzIGFuZCBQREFDIGNlbGxzLiAKCkFzIGZvciB0aGUgcHJpbWFyeSB0dW1vciBjZWxscywgd2Ugc2VlIGEgTWFjcm9waGFnZSBjbHVzdGVyIGFuZCBhbiBBY3RpdmF0ZWQgU3RlbGxhdGUgKHJlYWQ6IENBRikgY2x1c3RlciB3aGljaCBtYXRjaGVzIHRoZSBhdXRob3JzJyBhbm5vdGF0aW9ucy4gVGhlIGFjaW5hciBjZWxscyBhcmUgaW50ZXJlc3RpbmcsIGFzIHRoZSBhdXRob3JzIG5vdGVkIHRoYXQgb25lIG9mIHRoZSB0dW1vciBjbHVzdGVyIHdhcyBlbnJpY2hlZCBmb3IgYWNpbmFyIG1hcmtlciBnZW5lcy4gTW9zdCBsaWtlbHkgdGhvdWdoLCB0aGV5IGFyZSB0cnVseSBFcGl0aGVsaWFsIGNlbGxzIHdoaWNoIHdlJ2xsIGludmVzdGlnYXRlIGZ1cnRoZXIuIEZpbmFsbHksIHRoZSBzbWFsbCBFbmRvdGhlbGlhbCBjbHVzdGVyIHJvdWdobHkgbWF0Y2hlcyB0aGUgc2l6ZSBvZiB0aGUgRW5kb3RoZWxpYWwgY2x1c3RlciBpZGVudGlmaWVkIGJ5IHRoZSBhdXRob3JzLCB0aG91Z2ggaGVyZSBpdCdzIGxvY2F0ZWQgbmV4dCB0byB0aGUgcHV0YXRpdmUgRXBpdGhlbGlhbCBjZWxscyBpbnN0ZWFkIG9mIG5leHQgdG8gdGhlIENBRnMgYXMgaXQgd2FzIGluIHRoZSBvcmlnaW5hbCBwdWJsaWNhdGlvbi4gCgpgYGB7cn0KKHA3IC8gcDYpICsgcGxvdF9hbm5vdGF0aW9uKHRpdGxlID0gIlNpbmdsZVIgQW5ub3RhdGlvbnMiLCBzdWJ0aXRsZSA9ICJzY1JOQS1zZXEgUmVmZXJlbmNlIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICB0aGVtZSA9IHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE2LCBoanVzdCA9IDAuNSksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwbG90LnN1YnRpdGxlID0gZWxlbWVudF90ZXh0KGZhY2UgPSAiaXRhbGljIiwgaGp1c3QgPSAwLjUsIHNpemUgPSA5KSkpCmBgYAoKIyMjIFNpbmdsZVI6IEJ1bGsgUk5BLXNlcSBSZWZlcmVuY2UKCldlJ2xsIHBlcmZvcm0gdGhlIHNhbWUgcHJvY2VkdXJlLCB0aGlzIHRpbWUgdXNpbmcgYSBidWxrIFJOQS1zZXEgcmVmZXJlbmNlIC0gZGF0YSBmcm9tIHRoZSBIdW1hbiBQcmltYXJ5IENlbGwgQXRsYXMuIAoKYGBge3J9CmJ1bGtfcmVmIDwtIEh1bWFuUHJpbWFyeUNlbGxBdGxhc0RhdGEoKQpgYGAKCiMjIyMgUHJpbWFyeSBUdW1vcgoKV2UgcnVuIGBTaW5nbGVSYCBhZ2FpbiwgdGhpcyB0aW1lIHVzaW5nIHRoZSBidWxrIHJlZmVyZW5jZS4gCgpgYGB7cn0KY2VsbF9wcmVkcyA8LSBTaW5nbGVSKHRlc3QgPSBwcmltYXJ5QGFzc2F5cyRpbnRlZ3JhdGVkQHNjYWxlLmRhdGEsIAogICAgICAgICAgICAgICAgICAgICAgcmVmID0gYnVsa19yZWYsIAogICAgICAgICAgICAgICAgICAgICAgbGFiZWxzID0gYnVsa19yZWYkbGFiZWwubWFpbiwgCiAgICAgICAgICAgICAgICAgICAgICBjbHVzdGVycyA9IHByaW1hcnkkc2V1cmF0X2NsdXN0ZXJzLCAKICAgICAgICAgICAgICAgICAgICAgIGRlLm1ldGhvZCA9ICJ3aWxjb3giKQpwcmltYXJ5W1siU2luZ2xlUl9idWxrIl1dIDwtIGNlbGxfcHJlZHMkbGFiZWxzW21hdGNoKHByaW1hcnlbW11dW1sic2V1cmF0X2NsdXN0ZXJzIl1dLCByb3duYW1lcyhjZWxsX3ByZWRzKSldCnByaW1hcnlbWyJTaW5nbGVSX2J1bGsiXV0gPC0gY2FzZV93aGVuKHByaW1hcnlbWyJTaW5nbGVSX2J1bGsiXV0gPT0gIkVwaXRoZWxpYWxfY2VsbHMiIH4gIkVwaXRoZWxpYWwiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcHJpbWFyeVtbIlNpbmdsZVJfYnVsayJdXSA9PSAiRW5kb3RoZWxpYWxfY2VsbHMiIH4gIkVuZG90aGVsaWFsIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHByaW1hcnlbWyJTaW5nbGVSX2J1bGsiXV0gPT0gIkZpYnJvYmxhc3RzIiB+ICJGaWJyb2JsYXN0IiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHByaW1hcnlbWyJTaW5nbGVSX2J1bGsiXV0gPT0gIk5ldXRyb3BoaWxzIiB+ICJOZXV0cm9waGlsIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHByaW1hcnlbWyJTaW5nbGVSX2J1bGsiXV0gPT0gIk1hY3JvcGhhZ2UiIH4gIk1hY3JvcGhhZ2UiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcHJpbWFyeVtbIlNpbmdsZVJfYnVsayJdXSA9PSAiQk0gJiBQcm9nLiIgfiAiQm9uZSBNYXJyb3cgUHJvZ2VuaXRvciIpCnA4IDwtIERpbVBsb3QocHJpbWFyeSwgZ3JvdXAuYnkgPSAiU2luZ2xlUl9idWxrIikgKyAKICAgICAgbGFicyh4ID0gIlVNQVAgMSIsIHkgPSAiVU1BUCAyIiwgc3VidGl0bGUgPSAiUHJpbWFyeSBUdW1vciIpICsgCiAgICAgIHNjYWxlX2NvbG9yX3BhbGV0dGVlcl9kKCJnZ3RoZW1lczo6Q2xhc3NpY18yMCIpICsgCiAgICAgIHRoZW1lX3llaGxhYigpICsgCiAgICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJyaWdodCIsIAogICAgICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF9ibGFuaygpLCAKICAgICAgICAgICAgbGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDkpLCAKICAgICAgICAgICAgcGxvdC5zdWJ0aXRsZSA9IGVsZW1lbnRfdGV4dCgpLCAKICAgICAgICAgICAgYXhpcy50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAsIHNpemUgPSA5KSkgKyAKICAgICAgZ3VpZGVzKGNvbG9yID0gZ3VpZGVfbGVnZW5kKG5jb2wgPSAxLCBvdmVycmlkZS5hZXMgPSBsaXN0KHNpemUgPSAzKSkpCmBgYAoKIyMjIyBNZXRhc3RhdGljIFR1bW9yCgpXZSdsbCBkbyB0aGUgc2FtZSBmb3IgdGhlIG1ldGFzdGF0aWMgdHVtb3IgY2VsbHMuIAoKYGBge3J9CmNlbGxfcHJlZHMgPC0gU2luZ2xlUih0ZXN0ID0gbWV0YUBhc3NheXMkaW50ZWdyYXRlZEBzY2FsZS5kYXRhLCAKICAgICAgICAgICAgICAgICAgICAgIHJlZiA9IGJ1bGtfcmVmLCAKICAgICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IGJ1bGtfcmVmJGxhYmVsLm1haW4sIAogICAgICAgICAgICAgICAgICAgICAgY2x1c3RlcnMgPSBtZXRhJHNldXJhdF9jbHVzdGVycywgCiAgICAgICAgICAgICAgICAgICAgICBkZS5tZXRob2QgPSAid2lsY294IikKbWV0YVtbIlNpbmdsZVJfYnVsayJdXSA8LSBjZWxsX3ByZWRzJGxhYmVsc1ttYXRjaChtZXRhW1tdXVtbInNldXJhdF9jbHVzdGVycyJdXSwgcm93bmFtZXMoY2VsbF9wcmVkcykpXQptZXRhW1siU2luZ2xlUl9idWxrIl1dIDwtIGNhc2Vfd2hlbihtZXRhW1siU2luZ2xlUl9idWxrIl1dID09ICJFcGl0aGVsaWFsX2NlbGxzIiB+ICJFcGl0aGVsaWFsIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtZXRhW1siU2luZ2xlUl9idWxrIl1dID09ICJNb25vY3l0ZSIgfiAiTW9ub2N5dGUiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1ldGFbWyJTaW5nbGVSX2J1bGsiXV0gPT0gIlRfY2VsbHMiIH4gIlQiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1ldGFbWyJTaW5nbGVSX2J1bGsiXV0gPT0gIk5ldXJvbnMiIH4gIk5ldXJvbiIpCnA5IDwtIERpbVBsb3QobWV0YSwgZ3JvdXAuYnkgPSAiU2luZ2xlUl9idWxrIiwgY29scyA9IHBhbGV0dGVlcl9kKCJnZ3RoZW1lczo6Q2xhc3NpY18yMCIpWzg6MTFdKSArIAogICAgICBsYWJzKHN1YnRpdGxlID0gIk1ldGFzdGF0aWMgVHVtb3IiKSArIAogICAgICB0aGVtZV95ZWhsYWIoKSArIAogICAgICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAicmlnaHQiLCAKICAgICAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSwgCiAgICAgICAgICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X2JsYW5rKCksIAogICAgICAgICAgICBsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gOSksIAogICAgICAgICAgICBwbG90LnN1YnRpdGxlID0gZWxlbWVudF90ZXh0KCkpICsgCiAgICAgIGd1aWRlcyhjb2xvciA9IGd1aWRlX2xlZ2VuZChuY29sID0gMSwgb3ZlcnJpZGUuYWVzID0gbGlzdChzaXplID0gMykpKQpgYGAKCkxldCdzIGNoZWNrIG91dCB0aGUgcmVzdWx0cyEgRm9yIHRoZSBwcmltYXJ5IHR1bW9yIHNhbXBsZTogd2UgY2FuIGJlIGZhaXJseSBjZXJ0YWluIHRoYXQgdGhlIGxhcmdlIEVwaXRoZWxpYWwgY2x1c3RlciBpZGVudGl0eSBpcyBjb3JyZWN0LCBhcyB3ZSBzYXcgaXQgaW4gdGhlIHNpbmdsZSBjZWxsIGFuZCBidWxrIHJlZmVyZW5jZSBhbm5vdGF0aW9ucy4gSSB0aGluayB0aGUgQm9uZSBNYXJyb3cgUHJvZ2VuaXRvciBjbHVzdGVyIGlzIG1vc3QgbGlrZWx5IHRoZSBFcGl0aGVsaWFsICRccmlnaHRhcnJvdyQgTWVzZW5jaHltYWwgY2x1c3RlciB0aGUgYXV0aG9ycyBhbm5vdGF0ZWQuIFRoZSBGaWJyb2JsYXN0ICYgRW5kb3RoZWxpYWwgY2x1c3RlciBpZGVudGl0aWVzIGFsc28gbGluZSB1cCB3aXRoIHdoYXQgd2Ugc2F3IGluIHRoZSBzaW5nbGUgY2VsbCBhbm5vdGF0aW9ucy4gTGFzdGx5LCBJJ20gaW50ZXJlc3RlZCBpbiB0aGUgYW5ub3RhdGlvbiBvZiB0aGUgTmV1dHJvcGhpbCBjbHVzdGVyLCBhcyB0aGF0J3Mgc29tZXRoaW5nIHRoZSBhdXRob3JzIGRpZCBub3QgYW5ub3RhdGUuIAoKRm9yIHRoZSBtZXRhc3RhdGljIHNhbXBsZXMsIHdlIHNlZSBhZ2FpbiB0aGUgc21hbGwgVCAmIE1hY3JvcGhhZ2UgY2x1c3RlcnMuIFdlIGNhbiBiZSBwcmV0dHkgc3VyZSB0aGF0IHRob3NlIGFyZSBjb3JyZWN0LiBJIHRydXN0IHRoYXQgdGhlIGxhcmdlIEVwaXRoZWxpYWwgY2x1c3RlciBpcyBjb3JyZWN0LCB0aG91Z2ggSSdtIHB1enpsZWQgYXMgdG8gd2h5IHRoZSBmaW5hbCBjbHVzdGVyIHdhcyBhbm5vdGF0ZWQgYXMgY29udGFpbmluZyBOZXVyb25zLiBUaGUgc2luZ2xlIGNlbGwgcmVmZXJlbmNlIGRlZmluZWQgdGhhdCBjbHVzdGVyIGFzIEVuZG90aGVsaWFsLiBXZSdsbCBoYXZlIHRvIGRvIHNvbWUgZnVydGhlciBpbnZlc3RpZ2F0aW9uIG9uIHRoYXQgZ3JvdXAgb2YgY2VsbHMuIAoKYGBge3J9CihwOSAvIHA4KSArIHBsb3RfYW5ub3RhdGlvbih0aXRsZSA9ICJTaW5nbGVSIEFubm90YXRpb25zIiwgc3VidGl0bGUgPSAiQnVsayBSTkEtc2VxIFJlZmVyZW5jZSIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgdGhlbWUgPSB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNiwgaGp1c3QgPSAwLjUpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGxvdC5zdWJ0aXRsZSA9IGVsZW1lbnRfdGV4dChmYWNlID0gIml0YWxpYyIsIGhqdXN0ID0gMC41LCBzaXplID0gOSkpKQpgYGAKCiMjIyBQYXRod2F5IEFuYWx5c2lzIAoKIyMjIyBQcmltYXJ5CgpXZSdsbCB1c2UgW0RyLiBYaWFubHUgUGVuZydzIGBERUNPREVSYCBtZXRob2RdKGh0dHBzOi8vcHVibWVkLm5jYmkubmxtLm5paC5nb3YvMzE2MjgzMDAvKSBpbiBvcmRlciB0byBwZXJmb3JtIGEgcGFuY3JlYXMtc3BlY2lmaWMgTk1GIHZlcnNpb24gb2YgcGF0aHdheSBhbmFseXNpcywgaW4gd2hpY2ggZWFjaCBjZWxsIGlzIGFzc2lnbmVkIGNvbXBhcnRtZW50IHdlaWdodHMgZm9yIHBhdGh3YXlzIHN1Y2ggYXMgSW1tdW5lLCBDbGFzc2ljYWwgUERBQywgZXRjLiBUaGUgUERBQyBzdWJ0eXBlcyB1c2VkIGhlcmUsIEJhc2FsICYgQ2xhc3NpY2FsLCBhcmUgdGFrZW4gZnJvbSBbTW9mZml0dCAqZXQgYWwqICgyMDE1KV0oaHR0cHM6Ly93d3cubmF0dXJlLmNvbS9hcnRpY2xlcy9uZy4zMzk4KS4gCgpgYGB7cn0Kc2FtcGxlX3d0c191bnNjYWxlZCA8LSBEZWNvbl9zaW5nbGVfc2FtcGxlKCJUQ0dBX1JOQXNlcV9QQUFEIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwcmltYXJ5QGFzc2F5cyRpbnRlZ3JhdGVkQGRhdGEsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImdlbmVTeW1ib2wiKQpzYW1wbGVfd3RzIDwtIE5vcm1fUERBQ193ZWlnaHRzKHNhbXBsZV93dHNfdW5zY2FsZWQpCnByaW1hcnkgPC0gQWRkTWV0YURhdGEocHJpbWFyeSwgc2FtcGxlX3d0cyRJbW11bmUsICJpbW11bmUiKQpwcmltYXJ5IDwtIEFkZE1ldGFEYXRhKHByaW1hcnksIHNhbXBsZV93dHMkYmNSYXRpbywgImJjX3JhdGlvIikKcHJpbWFyeSA8LSBBZGRNZXRhRGF0YShwcmltYXJ5LCBzYW1wbGVfd3RzJEVuZG9jcmluZSwgImVuZG9jcmluZSIpCnByaW1hcnkgPC0gQWRkTWV0YURhdGEocHJpbWFyeSwgc2FtcGxlX3d0c191bnNjYWxlZFssIDldLCAiYmFzYWwiKQpwcmltYXJ5IDwtIEFkZE1ldGFEYXRhKHByaW1hcnksIHNhbXBsZV93dHNfdW5zY2FsZWRbLCA1XSwgImNsYXNzaWNhbCIpCnByaW1hcnkgPC0gQWRkTWV0YURhdGEocHJpbWFyeSwgc2FtcGxlX3d0cyROb3JtYWxTdHJvbWEsICJub3JtX3N0cm9tYSIpCnByaW1hcnkgPC0gQWRkTWV0YURhdGEocHJpbWFyeSwgc2FtcGxlX3d0cyRBY3RpdmF0ZWRTdHJvbWEsICJhY3Rfc3Ryb21hIikKYGBgCgpOb3cgbGV0J3MgZ2VuZXJhdGUgc29tZSBwbG90cyEgV2Ugc2VlIHRoYXQgdGhlIEltbXVuZSAmIEZpYnJvYmxhc3Qgc2NvcmVzIGNvcnJlc3BvbmQgd2l0aCB3aGF0IHdlIHNhdyBpbiBgU2luZ2xlUmAuIEludGVyZXN0aW5nbHksIGl0IGFwcGVhcnMgYXMgdGhvdWdoIHRoZSBQREFDIHNjb3JlcyBhcmUgaGlnaCBpbiBvbmx5IG9uZSBvZiB0aGUgRXBpdGhlbGlhbCBjbHVzdGVycyAoQ2x1c3RlciAxKS4gVGhpcyBsZWFkcyBtZSB0byBiZWxpZXZlIHRoYXQgdGhlcmUgYXJlIHBvdGVudGlhbCBzdWJjbHVzdGVycyB3aXRoaW4gdGhlIGNsdXN0ZXIsIGFuZCB0aGF0IHRoZSBvdGhlciBjbHVzdGVyIGlzIGNvbXBvc2VkIG9mIG5vcm1hbCwgbm9uLW1hbGlnbmFudCBFcGl0aGVsaWFsIGNlbGxzLiAKCmBgYHtyfQpwbG90cyA8LSBsaXN0KCkKdmFycyA8LSBjKCJpbW11bmUiLCAiZW5kb2NyaW5lIiwgImJhc2FsIiwgImNsYXNzaWNhbCIsICJub3JtX3N0cm9tYSIsICJhY3Rfc3Ryb21hIikKdGl0bGVzIDwtIGMoIkltbXVuZSIsICJFbmRvY3JpbmUiLCAiQmFzYWwgUERBQyIsICJDbGFzc2ljYWwgUERBQyIsICJOb3JtYWwgU3Ryb21hIiwgIkFjdGl2YXRlZCBTdHJvbWEiKQpmb3IgKGkgaW4gc2VxX2Fsb25nKHZhcnMpKSB7CiAgcGxvdHNbW2ldXSA8LSBGZWF0dXJlUGxvdChwcmltYXJ5LCBmZWF0dXJlcyA9IHZhcnNbaV0sIGNvbHMgPSBjKCJncmV5NzAiLCAiZmlyZWJyaWNrIikpICsgCiAgICAgICAgICAgICAgICBsYWJzKHN1YnRpdGxlID0gdGl0bGVzW2ldKSArIAogICAgICAgICAgICAgICAgdGhlbWVfeWVobGFiKCkgKyAKICAgICAgICAgICAgICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiwgCiAgICAgICAgICAgICAgICAgICAgICBheGlzLnRpdGxlID0gZWxlbWVudF9ibGFuaygpLCAKICAgICAgICAgICAgICAgICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X2JsYW5rKCksIAogICAgICAgICAgICAgICAgICAgICAgcGxvdC50aXRsZS5wb3NpdGlvbiA9ICJwbG90IiwgCiAgICAgICAgICAgICAgICAgICAgICBwbG90LnN1YnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41LCBzaXplID0gMTIpKQp9CnAxMCA8LSBnZ2FycmFuZ2UocGxvdGxpc3QgPSBwbG90cywgbmNvbCA9IDMsIG5yb3cgPSAyKSAlPiUgCiAgICAgICBhbm5vdGF0ZV9maWd1cmUoYm90dG9tID0gIlVNQVAgMSIsIGxlZnQgPSB0ZXh0X2dyb2IoIlVNQVAgMiIsIHJvdCA9IDM2MCksIAogICAgICAgICAgICAgICAgICAgICAgIHRvcCA9ICJERUNPREVSIFBhdGh3YXkgQW5hbHlzaXMgKFByaW1hcnkgVHVtb3IpIikKcDEwCmBgYAoKIyMjIyBNZXRhc3RhdGljIFR1bW9yCgpXZSdsbCByZXBlYXQgdGhlIGBERUNPREVSYCBhbmFseXNpcyBhbmQgcGxvdCBnZW5lcmF0aW9uIGZvciB0aGUgbWV0YXN0YXRpYyBjZWxscy4gCgpgYGB7cn0Kc2FtcGxlX3d0c191bnNjYWxlZCA8LSBEZWNvbl9zaW5nbGVfc2FtcGxlKCJUQ0dBX1JOQXNlcV9QQUFEIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtZXRhQGFzc2F5cyRpbnRlZ3JhdGVkQGRhdGEsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImdlbmVTeW1ib2wiKQpzYW1wbGVfd3RzIDwtIE5vcm1fUERBQ193ZWlnaHRzKHNhbXBsZV93dHNfdW5zY2FsZWQpCm1ldGEgPC0gQWRkTWV0YURhdGEobWV0YSwgc2FtcGxlX3d0cyRJbW11bmUsICJpbW11bmUiKQptZXRhIDwtIEFkZE1ldGFEYXRhKG1ldGEsIHNhbXBsZV93dHMkYmNSYXRpbywgImJjX3JhdGlvIikKbWV0YSA8LSBBZGRNZXRhRGF0YShtZXRhLCBzYW1wbGVfd3RzJEVuZG9jcmluZSwgImVuZG9jcmluZSIpCm1ldGEgPC0gQWRkTWV0YURhdGEobWV0YSwgc2FtcGxlX3d0c191bnNjYWxlZFssIDldLCAiYmFzYWwiKQptZXRhIDwtIEFkZE1ldGFEYXRhKG1ldGEsIHNhbXBsZV93dHNfdW5zY2FsZWRbLCA1XSwgImNsYXNzaWNhbCIpCm1ldGEgPC0gQWRkTWV0YURhdGEobWV0YSwgc2FtcGxlX3d0cyROb3JtYWxTdHJvbWEsICJub3JtX3N0cm9tYSIpCm1ldGEgPC0gQWRkTWV0YURhdGEobWV0YSwgc2FtcGxlX3d0cyRBY3RpdmF0ZWRTdHJvbWEsICJhY3Rfc3Ryb21hIikKYGBgCgpOb3cgbGV0J3MgZ2VuZXJhdGUgc29tZSBwbG90cyEgCgpgYGB7cn0KcGxvdHMgPC0gbGlzdCgpCnZhcnMgPC0gYygiaW1tdW5lIiwgImVuZG9jcmluZSIsICJiYXNhbCIsICJjbGFzc2ljYWwiLCAibm9ybV9zdHJvbWEiLCAiYWN0X3N0cm9tYSIpCnRpdGxlcyA8LSBjKCJJbW11bmUiLCAiRW5kb2NyaW5lIiwgIkJhc2FsIFBEQUMiLCAiQ2xhc3NpY2FsIFBEQUMiLCAiTm9ybWFsIFN0cm9tYSIsICJBY3RpdmF0ZWQgU3Ryb21hIikKZm9yIChpIGluIHNlcV9hbG9uZyh2YXJzKSkgewogIHBsb3RzW1tpXV0gPC0gRmVhdHVyZVBsb3QobWV0YSwgZmVhdHVyZXMgPSB2YXJzW2ldLCBjb2xzID0gYygiZ3JleTcwIiwgImZpcmVicmljayIpKSArIAogICAgICAgICAgICAgICAgbGFicyhzdWJ0aXRsZSA9IHRpdGxlc1tpXSkgKyAKICAgICAgICAgICAgICAgIHRoZW1lX3llaGxhYigpICsgCiAgICAgICAgICAgICAgICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIsIAogICAgICAgICAgICAgICAgICAgICAgYXhpcy50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSwgCiAgICAgICAgICAgICAgICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF9ibGFuaygpLCAKICAgICAgICAgICAgICAgICAgICAgIHBsb3QudGl0bGUucG9zaXRpb24gPSAicGxvdCIsIAogICAgICAgICAgICAgICAgICAgICAgcGxvdC5zdWJ0aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSwgc2l6ZSA9IDEyKSkKfQpgYGAKCkRlc3BpdGUgc29tZSBtaXNzaW5nIGdlbmVzLCB3ZSBzZWUgdGhhdCBvdXIgVCAmIE1hY3JvcGhhZ2UgYW5ub3RhdGlvbnMgbWF0Y2ggdGhlIEltbXVuZSB3ZWlnaHRzLCBhbmQgdGhhdCB0aGUgQmFzYWwgJiBDbGFzc2ljYWwgUERBQyBzY29yZXMgYXJlIGxvY2F0ZWQgaW4gdGhlIHB1dGF0aXZlIEVwaXRoZWxpYWwgY2x1c3RlcnMuIFNwZWNpZmljYWxseSwgdGhlIEJhc2FsIFBEQUMgd2VpZ2h0cyBhcmUgaGlnaGVzdCBpbiB0aGUgb2RkIGNsdXN0ZXIgdGhhdCB3YXMgYW5ub3RhdGVkIGFzIEdhbW1hIC8gTmV1cm9uIGNlbGxzIGJ5IGBTaW5nbGVSYC4gQWxsIHRocmVlIEVwaXRoZWxpYWwgY2x1c3RlcnMsIHRob3VnaCwgZG8gaGF2ZSBlaXRoZXIgQmFzYWwgb3IgQ2xhc3NpY2FsIFBEQUMgY2VsbHMgaW4gdGhlbS4gCgpgYGB7cn0KcDExIDwtIGdnYXJyYW5nZShwbG90bGlzdCA9IHBsb3RzLCBuY29sID0gMywgbnJvdyA9IDIpICU+JSAKICAgICAgIGFubm90YXRlX2ZpZ3VyZShib3R0b20gPSAiVU1BUCAxIiwgbGVmdCA9IHRleHRfZ3JvYigiVU1BUCAyIiwgcm90ID0gMzYwKSwgCiAgICAgICAgICAgICAgICAgICAgICAgdG9wID0gIkRFQ09ERVIgUGF0aHdheSBBbmFseXNpcyAoTWV0YXN0YXRpYyBUdW1vcikiKQpwMTEKYGBgCgojIyMgQXNzaWduIEJyb2FkIENlbGwgVHlwZXMKCldlIGhhdmUgZW5vdWdoIGluZm9ybWF0aW9uIHRvIGFjY3VyYXRlbHkgYXNzaWduIGJyb2FkIGNlbGwgdHlwZXMgdG8gb3VyIGNsdXN0ZXJzLiAKCiMjIyMgUHJpbWFyeSBUdW1vcgoKYGBge3J9CnByaW1hcnkkY2VsbF90eXBlIDwtIGNhc2Vfd2hlbihwcmltYXJ5JHNldXJhdF9jbHVzdGVycyA9PSAwIH4gIkVwaXRoZWxpYWwiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHByaW1hcnkkc2V1cmF0X2NsdXN0ZXJzID09IDEgfiAiRXBpdGhlbGlhbCIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcHJpbWFyeSRzZXVyYXRfY2x1c3RlcnMgPT0gMiB+ICJGaWJyb2JsYXN0IiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwcmltYXJ5JHNldXJhdF9jbHVzdGVycyA9PSAzIH4gIk1hY3JvcGhhZ2UiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHByaW1hcnkkc2V1cmF0X2NsdXN0ZXJzID09IDQgfiAiTmV1dHJvcGhpbCIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcHJpbWFyeSRzZXVyYXRfY2x1c3RlcnMgPT0gNSB+ICJNZXNlbmNoeW1hbCBTdGVtIENlbGwiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHByaW1hcnkkc2V1cmF0X2NsdXN0ZXJzID09IDYgfiAiRmlicm9ibGFzdCIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcHJpbWFyeSRzZXVyYXRfY2x1c3RlcnMgPT0gNyB+ICJFbmRvdGhlbGlhbCIpCmBgYAoKIyMjIyBNZXRhc3RhdGljIFR1bW9yCgpgYGB7cn0KbWV0YSRjZWxsX3R5cGUgPC0gY2FzZV93aGVuKG1ldGEkc2V1cmF0X2NsdXN0ZXJzID09IDAgfiAiRXBpdGhlbGlhbCIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgbWV0YSRzZXVyYXRfY2x1c3RlcnMgPT0gMSB+ICJFcGl0aGVsaWFsIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBtZXRhJHNldXJhdF9jbHVzdGVycyA9PSAyIH4gIkVwaXRoZWxpYWwiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1ldGEkc2V1cmF0X2NsdXN0ZXJzID09IDMgfiAiTWFjcm9waGFnZSIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgbWV0YSRzZXVyYXRfY2x1c3RlcnMgPT0gNCB+ICJUIikKYGBgCgojIyMgQ05WIEVzdGltYXRpb24KCk5leHQgd2UnbGwgYXR0ZW1wdCB0byBpZGVudGlmeSBtYWxpZ25hbnQgY2VsbHMgdXNpbmcgc2luZ2xlLWNlbGwgY29weSBudW1iZXIgdmFyaWF0aW9uIGVzdGltYXRpb24gYXMgaW1wbGVtZW50ZWQgaW4gdGhlIGBDT05DSVNtYXRgIHBhY2thZ2UuIERldGFpbHMgb2YgdGhlIEdNTSBtZXRob2RvbG9neSB1c2VkIGNhbiBiZSBmb3VuZCBbYXQgdGhlIERpYXogTGFiJ3MgR2l0SHViIHJlcG9zaXRvcnldKGh0dHBzOi8vZ2l0aHViLmNvbS9kaWF6bGFiL0NPTklDUykuCgojIyMjIFByaW1hcnkgVHVtb3IKCmBgYHtyfQpjaHJvbV9yZWdpb25zIDwtIHJlYWQudGFibGUoIi9Wb2x1bWVzL2xhYnMvSG9tZS9KZW4gSmVuIFllaCBMYWIvSmFjay9zY1JOQXNlcS9jaHJvbV9hcm1fcG9zaXRpb25zLnR4dCIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VwID0gIlx0IiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICByb3cubmFtZXMgPSAxLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGhlYWRlciA9IFRSVUUpCmdlbmVfcG9zIDwtIGdldEdlbmVQb3NpdGlvbnMocm93bmFtZXMocHJpbWFyeSkpCmNwbSA8LSB0KHQoYXMubWF0cml4KHByaW1hcnlAYXNzYXlzJFNDVEBjb3VudHMpKSAvIGNvbFN1bXMoYXMubWF0cml4KHByaW1hcnlAYXNzYXlzJFNDVEBjb3VudHMpKSkgKiAxMF41CmNwbSA8LSBsb2cyKGNwbSArIDEpCm5vcm1fZmFjdG9yIDwtIGNhbGNOb3JtRmFjdG9ycyhjcG0pCmNudl9lc3QgPC0gcGxvdEFsbChtYXQgPSBjcG0sIAogICAgICAgICAgICAgICAgICAgbm9ybUZhY3RvciA9IG5vcm1fZmFjdG9yLCAKICAgICAgICAgICAgICAgICAgIHJlZ2lvbnMgPSBjaHJvbV9yZWdpb25zLCAKICAgICAgICAgICAgICAgICAgIGdlbmVfcG9zID0gZ2VuZV9wb3MsIAogICAgICAgICAgICAgICAgICAgZm5hbWUgPSAiLi4vRGF0YS9QcmltYXJ5IikKYmljX3RhYmxlIDwtIHJlYWQudGFibGUoIi4uL0RhdGEvUHJpbWFyeV9CSUNfTFIudHh0IiwgCiAgICAgICAgICAgICAgICAgICAgICAgIHNlcCA9ICJcdCIsIAogICAgICAgICAgICAgICAgICAgICAgICByb3cubmFtZXMgPSAxLCAKICAgICAgICAgICAgICAgICAgICAgICAgaGVhZGVyID0gVFJVRSwgCiAgICAgICAgICAgICAgICAgICAgICAgIGNoZWNrLm5hbWVzID0gRkFMU0UpCmNhbmRfcmVnaW9ucyA8LSByb3duYW1lcyhiaWNfdGFibGVbYmljX3RhYmxlJGBCSUMgZGlmZmVyZW5jZWAgPiAyMDAgJiBiaWNfdGFibGUkYExSVCBhZGouIHAtdmFsYCA8IC4wMSwgXSkKYGBgCgpXZSB1c2UgJGsgPSAzJCBjbHVzdGVycywgYXMgd2UgYXNzdW1lIHRoYXQgdGhlIGNlbGxzIGNhbiBiZSBkaXZpZGVkIGludG8gMSkgUERBQyBjZWxscywgMikgQ0FGIGNlbGxzLCBhbmQgMykgbm9uLW1hbGlnbmFudCBjZWxscy4gV2Ugc2VlIHRoYXQgY2hyb21vc29tZXMgOXEsIDEycSwgYW5kIDE3cSBoYXZlIHRoZSBtb3N0IGVzdGltYXRlZCBDTlZzLiAKCmBgYHtyfQpoaXN0MSA8LSBwbG90SGlzdG9ncmFtKGNudl9lc3RbLCBjYW5kX3JlZ2lvbnNdLCAKICAgICAgICAgICAgICAgICAgICAgICBjcG0sIAogICAgICAgICAgICAgICAgICAgICAgIGNsdXN0ZXJzID0gMywgCiAgICAgICAgICAgICAgICAgICAgICAgenNjb3JlVGhyZXNob2xkID0gMywgCiAgICAgICAgICAgICAgICAgICAgICAgY2VsbHR5cGVzID0gcHJpbWFyeSRjZWxsX3R5cGUsIAogICAgICAgICAgICAgICAgICAgICAgIHBhdGllbnRzID0gcHJpbWFyeSRzYW1wbGUpCmBgYAoKYGBge3J9Cm5vcm1hbF9wcmltYXJ5IDwtIHdoaWNoKGhpc3QxID09IDEpCnByaW1hcnlAbWV0YS5kYXRhJENOViA8LSBpZmVsc2Uocm93bmFtZXMocHJpbWFyeUBtZXRhLmRhdGEpICVpbiUgbmFtZXMobm9ybWFsX3ByaW1hcnkpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiTm9ybWFsIiwgIk1hbGlnbmFudCIpCnAxMiA8LSBEaW1QbG90KHByaW1hcnksIGdyb3VwLmJ5ID0gIkNOViIpICsgCiAgICAgICBsYWJzKHN1YnRpdGxlID0gIlByaW1hcnkgVHVtb3IiKSArIAogICAgICAgdGhlbWVfeWVobGFiKCkgKyAKICAgICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiwgCiAgICAgICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF9ibGFuaygpLCAKICAgICAgICAgICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X2JsYW5rKCksIAogICAgICAgICAgICAgcGxvdC5zdWJ0aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpCmBgYAoKIyMjIyBNZXRhc3RhdGljIFR1bW9yCgpXZSByZXBlYXQgdGhlIHByb2Nlc3MgZm9yIHRoZSBtZXRhc3RhdGljIHR1bW9yIHNhbXBsZXMuIAoKYGBge3J9CmdlbmVfcG9zIDwtIGdldEdlbmVQb3NpdGlvbnMocm93bmFtZXMobWV0YSkpCmNwbSA8LSB0KHQoYXMubWF0cml4KG1ldGFAYXNzYXlzJFNDVEBjb3VudHMpKSAvIGNvbFN1bXMoYXMubWF0cml4KG1ldGFAYXNzYXlzJFNDVEBjb3VudHMpKSkgKiAxMF41CmNwbSA8LSBsb2cyKGNwbSArIDEpCm5vcm1fZmFjdG9yIDwtIGNhbGNOb3JtRmFjdG9ycyhjcG0pCmNudl9lc3QgPC0gcGxvdEFsbChtYXQgPSBjcG0sIAogICAgICAgICAgICAgICAgICAgbm9ybUZhY3RvciA9IG5vcm1fZmFjdG9yLCAKICAgICAgICAgICAgICAgICAgIHJlZ2lvbnMgPSBjaHJvbV9yZWdpb25zLCAKICAgICAgICAgICAgICAgICAgIGdlbmVfcG9zID0gZ2VuZV9wb3MsIAogICAgICAgICAgICAgICAgICAgZm5hbWUgPSAiLi4vRGF0YS9NZXRhIikKYmljX3RhYmxlIDwtIHJlYWQudGFibGUoIi4uL0RhdGEvTWV0YV9CSUNfTFIudHh0IiwgCiAgICAgICAgICAgICAgICAgICAgICAgIHNlcCA9ICJcdCIsIAogICAgICAgICAgICAgICAgICAgICAgICByb3cubmFtZXMgPSAxLCAKICAgICAgICAgICAgICAgICAgICAgICAgaGVhZGVyID0gVFJVRSwgCiAgICAgICAgICAgICAgICAgICAgICAgIGNoZWNrLm5hbWVzID0gRkFMU0UpCmNhbmRfcmVnaW9ucyA8LSByb3duYW1lcyhiaWNfdGFibGVbYmljX3RhYmxlJGBCSUMgZGlmZmVyZW5jZWAgPiAyMDAgJiBiaWNfdGFibGUkYExSVCBhZGouIHAtdmFsYCA8IC4wMSwgXSkKYGBgCgpXZSBzZWUgdGhhdCBjaHJvbW9zb21lcyAycCwgN3EsIDEycSwgYW5kIDE1cSBoYXZlIHRoZSBtb3N0IGVzdGltYXRlZCBDTlZzIGZvciB0aGUgbWV0YXN0YXRpYyBzYW1wbGVzLiAKCmBgYHtyfQpoaXN0MiA8LSBwbG90SGlzdG9ncmFtKGNudl9lc3RbLCBjYW5kX3JlZ2lvbnNdLCAKICAgICAgICAgICAgICAgICAgICAgICBjcG0sIAogICAgICAgICAgICAgICAgICAgICAgIGNsdXN0ZXJzID0gMywgCiAgICAgICAgICAgICAgICAgICAgICAgenNjb3JlVGhyZXNob2xkID0gMywgCiAgICAgICAgICAgICAgICAgICAgICAgY2VsbHR5cGVzID0gbWV0YSRjZWxsX3R5cGUsIAogICAgICAgICAgICAgICAgICAgICAgIHBhdGllbnRzID0gbWV0YSRzYW1wbGUpCmBgYAoKYGBge3J9Cm5vcm1hbF9tZXRhIDwtIHdoaWNoKGhpc3QyID09IDMpCm1ldGFAbWV0YS5kYXRhJENOViA8LSBpZmVsc2Uocm93bmFtZXMobWV0YUBtZXRhLmRhdGEpICVpbiUgbmFtZXMobm9ybWFsX21ldGEpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiTm9ybWFsIiwgIk1hbGlnbmFudCIpCnAxMyA8LSBEaW1QbG90KG1ldGEsIGdyb3VwLmJ5ID0gIkNOViIpICsgCiAgICAgICBsYWJzKHN1YnRpdGxlID0gIk1ldGFzdGF0aWMgVHVtb3IiKSArIAogICAgICAgdGhlbWVfeWVobGFiKCkgKyAKICAgICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiwgCiAgICAgICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF9ibGFuaygpLCAKICAgICAgICAgICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X2JsYW5rKCksIAogICAgICAgICAgICAgcGxvdC5zdWJ0aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpCmBgYAoKTGV0J3MgY2hlY2sgb3V0IHRoZSByZXN1bHRzISBXZSBzZWUgdGhhdCB0aGUgY2VsbHMgZXN0aW1hdGVkIHRvIGJlIG1hbGlnbmFudCBhcmUgbW9zdGx5IGxvY2F0ZWQgaW4gdGhlIEVwaXRoZWxpYWwgY2x1c3RlcnMuIFRoaXMgaXMgd2hhdCB3ZSdkIGV4cGVjdCBmcm9tIFBhbmNyZWF0aWMgKkR1Y3RhbCogQWRlbm9jYXJjaW5vbWEuIFRoZXJlIGFyZSBhbHNvIGEgZGVjZW50IGFtb3VudCBvZiBtYWxpZ25hbnQgY2VsbHMgaW4gdGhlIE1TQyBjbHVzdGVyIHdlIGlkZW50aWZpZWQuIE5vdGFibHksIHdlIGRvbid0IHNlZSBhIHNpZ25pZmljYW50IGNvbmNlbnRyYXRpb24gb2YgbWFsaWduYW50IGNlbGxzIGluIHRoZSBGaWJyb2JsYXN0IGNsdXN0ZXIgZnJvbSB0aGUgcHJpbWFyeSBzYW1wbGVzLiBUaGlzIG1ha2VzIG1lIGRvdWJ0IHRoZSBhdXRob3JzJyBhbm5vdGF0aW9uIG9mIHRob3NlIGNlbGxzIGFzIENhbmNlciBBc3NvY2lhdGVkIEZpYnJvYmxhc3RzIChDQUZzKS4gCgpgYGB7cn0KZ2dhcnJhbmdlKHAxMiwgcDEzLCBuY29sID0gMikgJT4lIAogIGFubm90YXRlX2ZpZ3VyZShib3R0b20gPSAiVU1BUCAxIiwgbGVmdCA9IHRleHRfZ3JvYigiVU1BUCAyIiwgcm90ID0gMzYwKSwgCiAgICAgICAgICAgICAgICAgIHRvcCA9ICJNYWxpZ25hbnQgQ2VsbHMgRXN0aW1hdGVkIFRocm91Z2ggQ09OSUNTbWF0IikKYGBgCgojIyMgUmVjbHVzdGVyaW5nIENlbGxzCgojIyMjIFByaW1hcnkgVHVtb3IgLSBFcGl0aGVsaWFsIENlbGxzCgpMYXN0bHksIHdlJ2xsIHVzZSBteSBtZXRob2QsIGBTQ0lTU09SU2AsIHRvIGRldGVybWluZSB3aGV0aGVyIHNlcGFyYXRlIEJhc2FsICYgQ2xhc3NpY2FsIFBEQUMgY2x1c3RlcnMgZXhpc3QgaW4gdGhlIFBEQUMgY2x1c3RlciAoQ2x1c3RlciAxKSBwcmVzZW50IGluIHRoZSBwcmltYXJ5IHR1bW9yIHNhbXBsZXMuIFRoZSBtZXRob2QgcmUtcHJvY2Vzc2VzIHRoZSBkYXRhIGFuZCB0ZXN0cyBzZXZlcmFsIGNsdXN0ZXJpbmcgcGFyYW1ldGVyIHNldHMgaW4gb3JkZXIgdG8gZGV0ZXJtaW5lIHdoaWNoIHBhcmFtZXRlciBzZXQgZml0cyB0aGUgY2VsbHMgdGhlIGJlc3QuIFRoaXMgaXMgZG9uZSBieSBtYXhpbWl6aW5nIHRoZSBtZWFuIHNpbGhvdWV0dGUgc2NvcmUgZm9yIGVhY2ggY2x1c3Rlci4gSGVyZSB3ZSBydW4gdGhlIG1ldGhvZCBhbmQgZ2VuZXJhdGUgc29tZSBwbG90cyBvZiB0aGUgcmVzdWx0cy4KCmBgYHtyfQplcGlfcmVjbHVzdCA8LSBSZWNsdXN0ZXJDZWxscyhzZXVyYXQub2JqZWN0ID0gcHJpbWFyeSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHdoaWNoLmNsdXN0ID0gMSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG4uSFZHID0gMzAwMCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG4uUEMgPSAxNSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGsudmFscyA9IGMoMjAsIDMwLCA0MCksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXNvbHV0aW9uLnZhbHMgPSBjKC4yLCAuMywgLjQpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVkby5lbWJlZGRpbmcgPSBUUlVFLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmFuZG9tLnNlZWQgPSA2MjkpCgpwMTQgPC0gRGltUGxvdChlcGlfcmVjbHVzdCkgKyAKICAgICAgIGxhYnMoc3VidGl0bGUgPSAiU0NJU1NPUlMgLSBDbHVzdGVyIDEiKSArIAogICAgICAgdGhlbWVfeWVobGFiKCkgKyAKICAgICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiwgCiAgICAgICAgICAgICBwbG90LnN1YnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSwgCiAgICAgICAgICAgICBheGlzLnRpdGxlID0gZWxlbWVudF9ibGFuaygpKSArIAogICAgICAgZ3VpZGVzKGNvbG9yID0gZ3VpZGVfbGVnZW5kKG5jb2wgPSAxLCBvdmVycmlkZS5hZXMgPSBsaXN0KHNpemUgPSAzKSkpCnAxNSA8LSBGZWF0dXJlUGxvdChlcGlfcmVjbHVzdCwgZmVhdHVyZXMgPSAiY2xhc3NpY2FsIiwgY29scyA9IGMoImdyZXk5MCIsICJmaXJlYnJpY2siKSkgKyAKICAgICAgIGxhYnMoc3VidGl0bGUgPSAiQ2xhc3NpY2FsIFBEQUMiKSArIAogICAgICAgdGhlbWVfeWVobGFiKCkgKyAKICAgICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiwKICAgICAgICAgICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X2JsYW5rKCksIAogICAgICAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSwgCiAgICAgICAgICAgICBwbG90LnN1YnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkKcDE2IDwtIEZlYXR1cmVQbG90KGVwaV9yZWNsdXN0LCBmZWF0dXJlcyA9ICJiYXNhbCIsIGNvbHMgPSBjKCJncmV5OTAiLCAiZmlyZWJyaWNrIikpICsgCiAgICAgICBsYWJzKHN1YnRpdGxlID0gIkJhc2FsIFBEQUMiKSArIAogICAgICAgdGhlbWVfeWVobGFiKCkgKyAKICAgICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiwKICAgICAgICAgICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X2JsYW5rKCksIAogICAgICAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSwgCiAgICAgICAgICAgICBwbG90LnN1YnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkKcDBhIDwtIHAwICsgCiAgICAgICBsYWJzKHN1YnRpdGxlID0gIlByaW1hcnkgVHVtb3IiKSArIAogICAgICAgdGhlbWUoYXhpcy50aXRsZS55ID0gZWxlbWVudF9ibGFuaygpLCAKICAgICAgICAgICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfYmxhbmsoKSwgCiAgICAgICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF9ibGFuaygpLCAKICAgICAgICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJyaWdodCIsIAogICAgICAgICAgICAgcGxvdC5zdWJ0aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpICsgCiAgICAgICBndWlkZXMoY29sb3IgPSBndWlkZV9sZWdlbmQobmNvbCA9IDEsIG92ZXJyaWRlLmFlcyA9IGxpc3Qoc2l6ZSA9IDMpKSkKcDE3IDwtIChwMTQgfCBwMGEpIC8gKHAxNSB8IHAxNikKYGBgCgpXZSBzZWUgdGhhdCB3aGlsZSBpdCdzIHBvc3NpYmxlIHRvIGZpbmQgc3ViY2x1c3RlcnMgd2l0aGluIENsdXN0ZXIgMSwgdGhlICJiZXN0IiBjbHVzdGVyaW5nIGRvZXMgbm90IHNlcGFyYXRlIG91dCB0aGUgQmFzYWwgJiBDbGFzc2ljYWwgY29tcG9uZW50cyBvZiB0aGUgUERBQyBjbHVzdGVyLiAKCmBgYHtyfQpnZ2FycmFuZ2UocDE3KSAlPiUgCiAgYW5ub3RhdGVfZmlndXJlKGJvdHRvbSA9ICJVTUFQIDEiLCBsZWZ0ID0gdGV4dF9ncm9iKCJVTUFQIDIiLCByb3QgPSAzNjApKQpgYGAKCiMjIyMgUHJpbWFyeSBUdW1vciAtIEZpYnJvYmxhc3RzCgpMYXN0bHksIHdlIHNhdyBpbiB0aGUgYERFQ09ERVJgIHJlc3VsdHMgdGhhdCBvbmUgY29ybmVyIG9mIHRoZSBGaWJyb2JsYXN0IGNsdXN0ZXIgaW4gdGhlIHByaW1hcnkgdHVtb3IgY2VsbHMgaGFkIGhpZ2ggRW5kb2NyaW5lIHdlaWdodHMuIFdlJ2xsIHJ1biBgU0NJU1NPUlNgIG9uIHRoYXQgY2x1c3RlciAoQ2x1c3RlciAyKSBpbiBvcmRlciB0byBzZWUgaWYgc3ViZ3JvdXBzIGNhbiBiZSBleHRyYWN0ZWQuIAoKYGBge3J9CmZpYnJvX3JlY2x1c3QgPC0gUmVjbHVzdGVyQ2VsbHMoc2V1cmF0Lm9iamVjdCA9IHByaW1hcnksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHdoaWNoLmNsdXN0ID0gMiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbi5IVkcgPSAzMDAwLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuLlBDID0gMTUsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGsudmFscyA9IGMoMTAsIDIwLCAzMCksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlc29sdXRpb24udmFscyA9IGMoLjIsIC4zLCAuNCksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlZG8uZW1iZWRkaW5nID0gVFJVRSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmFuZG9tLnNlZWQgPSA2MjkpCmZpYnJvX21hcmtlcnMgPC0gRmluZEFsbE1hcmtlcnMoZmlicm9fcmVjbHVzdCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdGVzdC51c2UgPSAid2lsY294IiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbG9nZmMudGhyZXNob2xkID0gMiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgb25seS5wb3MgPSBUUlVFLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2ZXJib3NlID0gRkFMU0UsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJhbmRvbS5zZWVkID0gNjI5KQpgYGAKCldlIGdlbmVyYXRlIHNvbWUgcGxvdHMgb2YgdGhlIHJlc3VsdHMuIAoKYGBge3J9CnAxOCA8LSBEaW1QbG90KGZpYnJvX3JlY2x1c3QpICsgCiAgICAgICBsYWJzKHN1YnRpdGxlID0gIlNDSVNTT1JTIC0gQ2x1c3RlciAyIikgKyAKICAgICAgIHRoZW1lX3llaGxhYigpICsgCiAgICAgICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIsIAogICAgICAgICAgICAgcGxvdC5zdWJ0aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSksIAogICAgICAgICAgICAgYXhpcy50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSkgKyAKICAgICAgIGd1aWRlcyhjb2xvciA9IGd1aWRlX2xlZ2VuZChuY29sID0gMSwgb3ZlcnJpZGUuYWVzID0gbGlzdChzaXplID0gMykpKQpwMTkgPC0gRmVhdHVyZVBsb3QoZmlicm9fcmVjbHVzdCwgZmVhdHVyZXMgPSAiYWN0X3N0cm9tYSIsIGNvbHMgPSBjKCJncmV5OTAiLCAiZmlyZWJyaWNrIikpICsgCiAgICAgICBsYWJzKHN1YnRpdGxlID0gIkFjdGl2YXRlZCBTdHJvbWEiKSArIAogICAgICAgdGhlbWVfeWVobGFiKCkgKyAKICAgICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiwKICAgICAgICAgICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X2JsYW5rKCksIAogICAgICAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSwgCiAgICAgICAgICAgICBwbG90LnN1YnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkKcDIwIDwtIEZlYXR1cmVQbG90KGZpYnJvX3JlY2x1c3QsIGZlYXR1cmVzID0gImVuZG9jcmluZSIsIGNvbHMgPSBjKCJncmV5OTAiLCAiZmlyZWJyaWNrIikpICsgCiAgICAgICBsYWJzKHN1YnRpdGxlID0gIkVuZG9jcmluZSIpICsgCiAgICAgICB0aGVtZV95ZWhsYWIoKSArIAogICAgICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLAogICAgICAgICAgICAgYXhpcy50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSwgCiAgICAgICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF9ibGFuaygpLCAKICAgICAgICAgICAgIHBsb3Quc3VidGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKQpwMjEgPC0gKHAxOCB8IHAwYSkgLyAocDE5IHwgcDIwKQpgYGAKCldlIHNlZSB0d28gY2x1c3RlcnMgLSBvbmUgb2Ygd2hpY2ggY2xlYXJseSBjb3JyZXNwb25kcyB0byBBY3RpdmF0ZWQgU3Ryb21hIC8gQ0FGIGNlbGxzLCBhbmQgdGhlIG90aGVyIG9mIHdoaWNoIHNlZW1zIHNvIGJlIGNvbXBvc2VkIG9mIGlzbGV0IGNlbGxzLCBhcyB0aGV5IGhhdmUgaGlnaCBlbmRvY3JpbmUgcGFuY3JlYXMgc2NvcmVzLiAKCmBgYHtyfQpnZ2FycmFuZ2UocDIxKSAlPiUgCiAgYW5ub3RhdGVfZmlndXJlKGJvdHRvbSA9ICJVTUFQIDEiLCBsZWZ0ID0gdGV4dF9ncm9iKCJVTUFQIDIiLCByb3QgPSAzNjApKQpgYGAKClRoZSB0b3AgNSBtYXJrZXJzIGZvciBlYWNoIGNsdXN0ZXIgYXJlIGEgbWl4dHVyZSBvZiBwcm90by1vbmNvZ2VuZXMgYW5kIHN0cm9tYS1yZWxhdGVkIGdlbmVzLiAKCmBgYHtyfQpmaWJyb19tYXJrZXJzICU+JSAKICBkcGx5cjo6c2VsZWN0KGNsdXN0ZXIsIGdlbmUsIGF2Z19sb2cyRkMsIHBfdmFsX2FkaiwgcGN0LjEsIHBjdC4yKSAlPiUgCiAgZ3JvdXBfYnkoY2x1c3RlcikgJT4lIAogIHNsaWNlX2hlYWQobiA9IDUpICU+JSAKICBrYmwoYm9va3RhYnMgPSBUUlVFLCBjb2wubmFtZXMgPSBjKCJDbHVzdGVyIiwgIkdlbmUiLCAiTWVhbiBMb2cyRkMiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJBZGouIFAtdmFsdWUiLCAiJSBFeHByZXNzZWQgMSIsICIlIEV4cHJlc3NlZCAyIikpICU+JSAKICBrYWJsZV9taW5pbWFsKCJob3ZlciIsIGZ1bGxfd2lkdGggPSBGQUxTRSkKYGBgCgoKIyMjIEFzc2lnbiBDZWxsIFR5cGVzCgpGaW5hbGx5LCB3ZSBoYXZlIGVub3VnaCBpbmZvcm1hdGlvbiB0byBhY2N1cmF0ZWx5IGFzc2lnbiBjZWxsIHR5cGVzIHRvIG91ciBjbHVzdGVycy4gCgojIyMjIFByaW1hcnkgVHVtb3IKCmBgYHtyfQplbmRvY3JpbmUgPC0gbmFtZXMoZmlicm9fcmVjbHVzdCRzZXVyYXRfY2x1c3RlcnNbZmlicm9fcmVjbHVzdCRzZXVyYXRfY2x1c3RlcnMgPT0gMV0pCkNBRiA8LSBuYW1lcyhmaWJyb19yZWNsdXN0JHNldXJhdF9jbHVzdGVyc1tmaWJyb19yZWNsdXN0JHNldXJhdF9jbHVzdGVycyA9PSAwXSkKcHJpbWFyeSRjZWxsX3R5cGUgPC0gY2FzZV93aGVuKHJvd25hbWVzKHByaW1hcnlAbWV0YS5kYXRhKSAlaW4lIGVuZG9jcmluZSB+ICJFbmRvY3JpbmUiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJvd25hbWVzKHByaW1hcnlAbWV0YS5kYXRhKSAlaW4lIENBRiB+ICJDQUYiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHByaW1hcnkkc2V1cmF0X2NsdXN0ZXJzID09IDEgfiAiUERBQyIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVFJVRSB+IHByaW1hcnkkY2VsbF90eXBlKQpJZGVudHMocHJpbWFyeSkgPC0gImNlbGxfdHlwZSIKcDIyIDwtIERpbVBsb3QocHJpbWFyeSwgZ3JvdXAuYnkgPSAiY2VsbF90eXBlIikgKyAKICAgICAgIHNjYWxlX2NvbG9yX3BhbGV0dGVlcl9kKCJnZ3RoZW1lczo6Q2xhc3NpY18yMCIpICsgCiAgICAgICBsYWJzKHN1YnRpdGxlID0gIlByaW1hcnkgVHVtb3IiLCB4ID0gIlVNQVAgMSIsIHkgPSAiVU1BUCAyIikgKyAKICAgICAgIHRoZW1lX3llaGxhYigpICsgCiAgICAgICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAicmlnaHQiLCAKICAgICAgICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X2JsYW5rKCksIAogICAgICAgICAgICAgbGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSwgCiAgICAgICAgICAgICBwbG90LnN1YnRpdGxlID0gZWxlbWVudF90ZXh0KCksIAogICAgICAgICAgICAgYXhpcy50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAsIHNpemUgPSA5KSkgKyAKICAgICAgIGd1aWRlcyhjb2xvciA9IGd1aWRlX2xlZ2VuZChuY29sID0gMSwgb3ZlcnJpZGUuYWVzID0gbGlzdChzaXplID0gMykpKQpgYGAKCiMjIyMgTWV0YXN0YXRpYyBUdW1vcgoKYGBge3J9Cm1ldGEkY2VsbF90eXBlIDwtIGNhc2Vfd2hlbihtZXRhJHNldXJhdF9jbHVzdGVycyA9PSAwIH4gIlBEQUMiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1ldGEkc2V1cmF0X2NsdXN0ZXJzID09IDEgfiAiUERBQyIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgbWV0YSRzZXVyYXRfY2x1c3RlcnMgPT0gMiB+ICJQREFDIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBtZXRhJHNldXJhdF9jbHVzdGVycyA9PSAzIH4gIk1hY3JvcGhhZ2UiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1ldGEkc2V1cmF0X2NsdXN0ZXJzID09IDQgfiAiVCIpCklkZW50cyhtZXRhKSA8LSAiY2VsbF90eXBlIgpwMjMgPC0gRGltUGxvdChtZXRhLCBncm91cC5ieSA9ICJjZWxsX3R5cGUiLCBjb2xzID0gcGFsZXR0ZWVyX2QoImdndGhlbWVzOjpDbGFzc2ljXzIwIilbYyg2LCA5LCAxMSldKSArIAogICAgICAgbGFicyhzdWJ0aXRsZSA9ICJNZXRhc3RhdGljIFR1bW9yIikgKyAKICAgICAgIHRoZW1lX3llaGxhYigpICsgCiAgICAgICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAicmlnaHQiLCAKICAgICAgICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X2JsYW5rKCksIAogICAgICAgICAgICAgYXhpcy50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSwgCiAgICAgICAgICAgICBsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gOSksIAogICAgICAgICAgICAgcGxvdC5zdWJ0aXRsZSA9IGVsZW1lbnRfdGV4dCgpKSArIAogICAgICAgZ3VpZGVzKGNvbG9yID0gZ3VpZGVfbGVnZW5kKG5jb2wgPSAxLCBvdmVycmlkZS5hZXMgPSBsaXN0KHNpemUgPSAzKSkpCmBgYAoKTGV0J3MgY2hlY2sgb3V0IHRoZSBmaW5hbCByZXN1bHRzISAKCmBgYHtyfQoocDIzIC8gcDIyKSArIHBsb3RfYW5ub3RhdGlvbih0aXRsZSA9ICJGaW5hbCBDZWxsdHlwZSBMYWJlbHMiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdGhlbWUgPSB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNiwgaGp1c3QgPSAwLjUpKSkKYGBgCgojIyBEaWZmZXJlbnRpYWwgRXhwcmVzc2lvbiAKCkxhc3RseSwgd2UnbGwgcnVuIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIHRlc3RzIGFnYWluIHVzaW5nIG91ciBmaW5hbCBjZWxsIGxhYmVscyB0aGlzIHRpbWUgaW5zdGVhZCBvZiBvdXIgY2x1c3RlciBpZGVudGl0aWVzLgoKIyMjIFByaW1hcnkgVHVtb3IgCgpgYGB7cn0KcHJpbWFyeV9tYXJrZXJzX2ZpbmFsIDwtIEZpbmRBbGxNYXJrZXJzKHByaW1hcnksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbG9nZmMudGhyZXNob2xkID0gMiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0ZXN0LnVzZSA9ICJ3aWxjb3giLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZlcmJvc2UgPSBGQUxTRSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBvbmx5LnBvcyA9IFRSVUUsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmFuZG9tLnNlZWQgPSA2MjkpCnByaW1hcnlfbWFya2Vyc19maW5hbF90b3A1IDwtIHByaW1hcnlfbWFya2Vyc19maW5hbCAlPiUgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdyb3VwX2J5KGNsdXN0ZXIpICU+JSAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2xpY2VfaGVhZChuID0gNSkKcDI0IDwtIERvdFBsb3QocHJpbWFyeSwgZmVhdHVyZXMgPSBwcmltYXJ5X21hcmtlcnNfZmluYWxfdG9wNSRnZW5lLCBjb2xzID0gYygiZ3JleTkwIiwgImZpcmVicmljayIpKSArIAogICAgICAgbGFicyh4ID0gIkdlbmUiLCAKICAgICAgICAgICAgeSA9ICJDZWxsIiwgCiAgICAgICAgICAgIGNvbG9yID0gIk1lYW4gRXhwcmVzc2lvbiIsIAogICAgICAgICAgICBzaXplID0gIlBlcmNlbnQgRXhwcmVzc2VkIiwgCiAgICAgICAgICAgIHRpdGxlID0gIlByaW1hcnkgVHVtb3IgTWFya2VyIEdlbmVzIikgKyAKICAgICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iLCAKICAgICAgICAgICAgIGF4aXMubGluZSA9IGVsZW1lbnRfYmxhbmsoKSwgCiAgICAgICAgICAgICBsZWdlbmQuanVzdGlmaWNhdGlvbiA9ICJjZW50ZXIiLCAKICAgICAgICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpLCAKICAgICAgICAgICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfYmxhbmsoKSwgCiAgICAgICAgICAgICBwYW5lbC5ib3JkZXIgPSBlbGVtZW50X3JlY3QoZmlsbCA9IE5BLCBzaXplID0gMSwgY29sb3IgPSAiYmxhY2siKSwgCiAgICAgICAgICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCBzaXplID0gNy41LCB2anVzdCA9IDEsIGhqdXN0ID0gMSksIAogICAgICAgICAgICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDgpKSArIAogICAgICAgZ3VpZGVzKGNvbG9yID0gZ3VpZGVfY29sb3JiYXIodGl0bGUucG9zaXRpb24gPSAidG9wIiwgdGl0bGUuaGp1c3QgPSAwLjUsIGJhcndpZHRoID0gdW5pdCg1LCAiY20iKSksIAogICAgICAgICAgICAgIHNpemUgPSBndWlkZV9sZWdlbmQodGl0bGUucG9zaXRpb24gPSAidG9wIiwgdGl0bGUuaGp1c3QgPSAwLjUpKQpgYGAKCk91ciBwcmltYXJ5IHR1bW9yIGNsdXN0ZXJzIGFyZSBmYWlybHkgd2VsbC1zZXBhcmF0ZWQuIAoKYGBge3J9CnAyNApgYGAKCiMjIyBNZXRhc3RhdGljIFR1bW9yIAoKYGBge3J9Cm1ldGFfbWFya2Vyc19maW5hbCA8LSBGaW5kQWxsTWFya2VycyhtZXRhLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxvZ2ZjLnRocmVzaG9sZCA9IDIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdGVzdC51c2UgPSAid2lsY294IiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2ZXJib3NlID0gRkFMU0UsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgb25seS5wb3MgPSBUUlVFLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJhbmRvbS5zZWVkID0gNjI5KQptZXRhX21hcmtlcnNfZmluYWxfdG9wNyA8LSBtZXRhX21hcmtlcnNfZmluYWwgJT4lIAogICAgICAgICAgICAgICAgICAgICAgICAgICBncm91cF9ieShjbHVzdGVyKSAlPiUgCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHNsaWNlX2hlYWQobiA9IDcpCnAyNSA8LSBEb3RQbG90KG1ldGEsIGZlYXR1cmVzID0gdW5pcXVlKG1ldGFfbWFya2Vyc19maW5hbF90b3A3JGdlbmUpLCBjb2xzID0gYygiZ3JleTkwIiwgImZpcmVicmljayIpKSArIAogICAgICAgbGFicyh4ID0gIkdlbmUiLCAKICAgICAgICAgICAgeSA9ICJDbHVzdGVyIiwgCiAgICAgICAgICAgIGNvbG9yID0gIk1lYW4gRXhwcmVzc2lvbiIsIAogICAgICAgICAgICBzaXplID0gIlBlcmNlbnQgRXhwcmVzc2VkIiwgCiAgICAgICAgICAgIHRpdGxlID0gIk1ldGFzdGF0aWMgVHVtb3IgTWFya2VyIEdlbmVzIikgKyAKICAgICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iLCAKICAgICAgICAgICAgIGF4aXMubGluZSA9IGVsZW1lbnRfYmxhbmsoKSwgCiAgICAgICAgICAgICBsZWdlbmQuanVzdGlmaWNhdGlvbiA9ICJjZW50ZXIiLCAKICAgICAgICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpLCAKICAgICAgICAgICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfYmxhbmsoKSwgCiAgICAgICAgICAgICBwYW5lbC5ib3JkZXIgPSBlbGVtZW50X3JlY3QoZmlsbCA9IE5BLCBzaXplID0gMSwgY29sb3IgPSAiYmxhY2siKSwgCiAgICAgICAgICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCBzaXplID0gOCwgdmp1c3QgPSAxLCBoanVzdCA9IDEpKSArIAogICAgICAgZ3VpZGVzKGNvbG9yID0gZ3VpZGVfY29sb3JiYXIodGl0bGUucG9zaXRpb24gPSAidG9wIiwgdGl0bGUuaGp1c3QgPSAwLjUsIGJhcndpZHRoID0gdW5pdCg1LCAiY20iKSksIAogICAgICAgICAgICAgIHNpemUgPSBndWlkZV9sZWdlbmQodGl0bGUucG9zaXRpb24gPSAidG9wIiwgdGl0bGUuaGp1c3QgPSAwLjUpKQpgYGAKCldlIGFsc28gaGF2ZSBnb29kIHNlcGFyYXRpb24gYmV0d2VlbiBjZWxsdHlwZXMgZm9yIHRoZSBtZXRhc3RhdGljIHNhbXBsZXMuIAoKYGBge3J9CnAyNQpgYGAKCiMgU2F2ZSBSZXN1bHRzCgpgYGB7ciwgZXZhbD1GQUxTRX0Kc2F2ZVJEUyhwcmltYXJ5LCBmaWxlID0gIn4vRGVza3RvcC9wcmltYXJ5LlJkcyIpCnNhdmVSRFMobWV0YSwgZmlsZSA9ICJ+L0Rlc2t0b3AvbWV0YS5SZHMiKQpgYGAK